[git] GnuPG - branch, master, updated. gnupg-2.1.0beta3-369-g0724328

by Werner Koch cvs at cvs.gnupg.org
Sat Mar 22 21:13:41 CET 2014


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  072432883ededa15bf35f80102e0572746ba4af1 (commit)
       via  a77ed0f266d03e234027dda4de5a7f3dd6787b1e (commit)
       via  6376227a31b3076321ce16ad626b333057bda53d (commit)
      from  6c058fac65c7e9d1ffb72686f0f02644f172da22 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 072432883ededa15bf35f80102e0572746ba4af1
Author: Werner Koch <wk at gnupg.org>
Date:   Sat Mar 22 21:12:46 2014 +0100

    agent: Support the Ed25519 signature algorithm for ssh.
    
    * agent/command-ssh.c (SPEC_FLAG_IS_EdDSA): New.
    (ssh_key_types): Add entry for ssh-ed25519.
    (ssh_identifier_from_curve_name): Move to the top.
    (stream_read_skip): New.
    (stream_read_blob): New.
    (ssh_signature_encoder_rsa): Replace MPIS array by an s-exp and move
    the s-exp parsing to here.
    (ssh_signature_encoder_dsa): Ditto.
    (ssh_signature_encoder_ecdsa): Ditto.
    (ssh_signature_encoder_eddsa): New.
    (sexp_key_construct): Rewrite.
    (ssh_key_extract): Rename to ...
    (ssh_key_to_blob): .. this and rewrite most of it.
    (ssh_receive_key): Add case for EdDSA.
    (ssh_convert_key_to_blob, key_secret_to_public): Remove.
    (ssh_send_key_public): Rewrite.
    (ssh_handler_request_identities): Simplify.
    (data_sign): Add rename args.  Add new args HASH and HASHLEN.  Make
    use of es_fopenmen and es_fclose_snatch.  Remove parsing into MPIs
    which is now doe in the sgnature encoder functions.
    (ssh_handler_sign_request): Take care of Ed25519.
    (ssh_key_extract_comment): Rewrite using gcry_sexp_nth_string.
    --
    
    To make the code easier readable most of the Ed25591 work has been
    done using a new explicit code path.  Warning: Libgcrypt 1.6.1 uses a
    non optimized implementation for Ed25519 and timing attacks might be
    possible.
    
    While working on the code I realized that it could need more rework;
    it is at some places quite baroque and more complicated than needed.
    Given that we require Libgcrypt 1.6 anyway, we should make more use of
    modern Libgcrypt functions.

diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index fb3b29a..be2ab3b 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1,6 +1,6 @@
 /* command-ssh.c - gpg-agent's ssh-agent emulation layer
  * Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc.
- * Copyright (C) 2013 Werner Koch
+ * Copyright (C) 2013, 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -74,6 +74,7 @@
 #define SSH_DSA_SIGNATURE_ELEMS    2
 #define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
 #define SPEC_FLAG_IS_ECDSA    (1 << 1)
+#define SPEC_FLAG_IS_EdDSA    (1 << 2)  /*(lowercase 'd' on purpose.)*/
 
 /* The name of the control file.  */
 #define SSH_CONTROL_FILE_NAME "sshcontrol"
@@ -138,7 +139,7 @@ typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
    functions are necessary.  */
 typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec,
                                                 estream_t signature_blob,
-						gcry_mpi_t *mpis);
+						gcry_sexp_t sig);
 
 /* Type, which is used for boundling all the algorithm specific
    information together in a single object.  */
@@ -229,13 +230,17 @@ static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
 static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
 static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
                                               estream_t signature_blob,
-                                              gcry_mpi_t *mpis);
+                                              gcry_sexp_t signature);
 static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
                                               estream_t signature_blob,
-                                              gcry_mpi_t *mpis);
+                                              gcry_sexp_t signature);
 static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
                                                 estream_t signature_blob,
-                                                gcry_mpi_t *mpis);
+                                                gcry_sexp_t signature);
+static gpg_error_t ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
+                                                estream_t signature_blob,
+                                                gcry_sexp_t signature);
+static gpg_error_t ssh_key_extract_comment (gcry_sexp_t key, char **comment);
 
 
 
@@ -266,9 +271,14 @@ static ssh_request_spec_t request_specs[] =
 static ssh_key_type_spec_t ssh_key_types[] =
   {
     {
+      "ssh-ed25519", "ecc", "qd",  "q", "rs", "qd",
+      NULL,                 ssh_signature_encoder_eddsa,
+      "Ed25519", 0,               SPEC_FLAG_IS_EdDSA
+    },
+    {
       "ssh-rsa", "rsa", "nedupq", "en",   "s",  "nedpqu",
       ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
-      NULL, 0, SPEC_FLAG_USE_PKCS1V2
+      NULL, 0,                    SPEC_FLAG_USE_PKCS1V2
     },
     {
       "ssh-dss", "dsa", "pqgyx",  "pqgy", "rs", "pqgyx",
@@ -290,7 +300,6 @@ static ssh_key_type_spec_t ssh_key_types[] =
       NULL,                 ssh_signature_encoder_ecdsa,
       "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
     }
-
   };
 
 

@@ -336,7 +345,20 @@ make_cstring (const char *data, size_t data_n)
   return s;
 }
 
+/* Lookup the ssh-identifier for the ECC curve CURVE_NAME.  Returns
+   NULL if not found.  */
+static const char *
+ssh_identifier_from_curve_name (const char *curve_name)
+{
+  int i;
+
+  for (i = 0; i < DIM (ssh_key_types); i++)
+    if (ssh_key_types[i].curve_name
+        && !strcmp (ssh_key_types[i].curve_name, curve_name))
+      return ssh_key_types[i].ssh_identifier;
 
+  return NULL;
+}
 
 
 /*
@@ -459,6 +481,34 @@ stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
   return err;
 }
 
+/* Skip over SIZE bytes from STREAM.  */
+static gpg_error_t
+stream_read_skip (estream_t stream, size_t size)
+{
+  char buffer[128];
+  size_t bytes_to_read, bytes_read;
+  int ret;
+
+  do
+    {
+      bytes_to_read = size;
+      if (bytes_to_read > sizeof buffer)
+        bytes_to_read = sizeof buffer;
+
+      ret = es_read (stream, buffer, bytes_to_read, &bytes_read);
+      if (ret)
+        return gpg_error_from_syserror ();
+      else if (bytes_read != bytes_to_read)
+        return gpg_error (GPG_ERR_EOF);
+      else
+        size -= bytes_to_read;
+    }
+  while (size);
+
+  return 0;
+}
+
+
 /* Write SIZE bytes from BUFFER to STREAM.  */
 static gpg_error_t
 stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
@@ -525,21 +575,68 @@ stream_read_string (estream_t stream, unsigned int secure,
   return err;
 }
 
-/* Read a C-string from STREAM, store copy in STRING.  */
+
+/* Read a binary string from STREAM and store it as an opaque MPI at
+   R_MPI.  Depending on SECURE use secure memory.  If the string is
+   too large for key material return an error.  */
 static gpg_error_t
-stream_read_cstring (estream_t stream, char **string)
+stream_read_blob (estream_t stream, unsigned int secure, gcry_mpi_t *r_mpi)
 {
-  unsigned char *buffer;
   gpg_error_t err;
+  unsigned char *buffer = NULL;
+  u32 length = 0;
 
-  err = stream_read_string (stream, 0, &buffer, NULL);
+  *r_mpi = NULL;
+
+  /* Read string length.  */
+  err = stream_read_uint32 (stream, &length);
   if (err)
-    goto out;
+    goto leave;
+
+  /* To avoid excessive use of secure memory we check that an MPI is
+     not too large. */
+  if (length > (4096/8) + 8)
+    {
+      log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
+      err = GPG_ERR_TOO_LARGE;
+      goto leave;
+    }
 
-  *string = (char *) buffer;
+  /* Allocate space.  */
+  if (secure)
+    buffer = xtrymalloc_secure (length? length:1);
+  else
+    buffer = xtrymalloc (length?length:1);
+  if (!buffer)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
 
- out:
+  /* Read data.  */
+  err = stream_read_data (stream, buffer, length);
+  if (err)
+    goto leave;
 
+  *r_mpi = gcry_mpi_set_opaque (NULL, buffer, 8*length);
+  buffer = NULL;
+
+ leave:
+  xfree (buffer);
+  return err;
+}
+
+
+/* Read a C-string from STREAM, store copy in STRING.  */
+static gpg_error_t
+stream_read_cstring (estream_t stream, char **string)
+{
+  gpg_error_t err;
+  unsigned char *buffer;
+
+  err = stream_read_string (stream, 0, &buffer, NULL);
+  if (!err)
+    *string = (char *)buffer;
   return err;
 }
 
@@ -635,6 +732,7 @@ stream_write_mpi (estream_t stream, gcry_mpi_t mpint)
   return err;
 }
 
+
 /* Copy data from SRC to DST until EOF is reached.  */
 static gpg_error_t
 stream_copy (estream_t dst, estream_t src)
@@ -753,7 +851,7 @@ open_control_file (ssh_control_file_t *r_cf, int append)
       err = gpg_error_from_syserror ();
       goto leave;
     }
-  /* FIXME: With "a+" we are not able to check whether this will will
+  /* FIXME: With "a+" we are not able to check whether this will
      be created and thus the blurb needs to be written first.  */
   cf->fp = fopen (cf->fname, append? "a+":"r");
   if (!cf->fp && errno == ENOENT)
@@ -1245,15 +1343,63 @@ ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
 /* Signature encoder function for RSA.  */
 static gpg_error_t
 ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
-                           estream_t signature_blob, gcry_mpi_t *mpis)
+                           estream_t signature_blob,
+                           gcry_sexp_t s_signature)
 {
+  gpg_error_t err = 0;
+  gcry_sexp_t valuelist = NULL;
+  gcry_sexp_t sublist = NULL;
+  gcry_mpi_t sig_value = NULL;
+  gcry_mpi_t *mpis = NULL;
+  const char *elems;
+  size_t elems_n;
+  int i;
+
   unsigned char *data;
   size_t data_n;
-  gpg_error_t err;
   gcry_mpi_t s;
 
-  (void)spec;
+  valuelist = gcry_sexp_nth (s_signature, 1);
+  if (!valuelist)
+    {
+      err = gpg_error (GPG_ERR_INV_SEXP);
+      goto out;
+    }
+
+  elems = spec->elems_signature;
+  elems_n = strlen (elems);
+
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+  if (!mpis)
+    {
+      err = gpg_error_from_syserror ();
+      goto out;
+    }
+
+  for (i = 0; i < elems_n; i++)
+    {
+      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+      if (!sublist)
+	{
+	  err = gpg_error (GPG_ERR_INV_SEXP);
+	  break;
+	}
+
+      sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+      if (!sig_value)
+	{
+	  err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
+	  break;
+	}
+      gcry_sexp_release (sublist);
+      sublist = NULL;
+
+      mpis[i] = sig_value;
+    }
+  if (err)
+    goto out;
 
+  /* RSA specific */
   s = mpis[0];
 
   err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
@@ -1264,7 +1410,9 @@ ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
   xfree (data);
 
  out:
-
+  gcry_sexp_release (valuelist);
+  gcry_sexp_release (sublist);
+  mpint_list_free (mpis);
   return err;
 }
 
@@ -1272,17 +1420,63 @@ ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
 /* Signature encoder function for DSA.  */
 static gpg_error_t
 ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
-                           estream_t signature_blob, gcry_mpi_t *mpis)
+                           estream_t signature_blob,
+                           gcry_sexp_t s_signature)
 {
+  gpg_error_t err = 0;
+  gcry_sexp_t valuelist = NULL;
+  gcry_sexp_t sublist = NULL;
+  gcry_mpi_t sig_value = NULL;
+  gcry_mpi_t *mpis = NULL;
+  const char *elems;
+  size_t elems_n;
+  int i;
+
   unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
-  unsigned char *data;
+  unsigned char *data = NULL;
   size_t data_n;
-  gpg_error_t err;
-  int i;
 
-  (void)spec;
+  valuelist = gcry_sexp_nth (s_signature, 1);
+  if (!valuelist)
+    {
+      err = gpg_error (GPG_ERR_INV_SEXP);
+      goto out;
+    }
+
+  elems = spec->elems_signature;
+  elems_n = strlen (elems);
+
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+  if (!mpis)
+    {
+      err = gpg_error_from_syserror ();
+      goto out;
+    }
+
+  for (i = 0; i < elems_n; i++)
+    {
+      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+      if (!sublist)
+	{
+	  err = gpg_error (GPG_ERR_INV_SEXP);
+	  break;
+	}
+
+      sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+      if (!sig_value)
+	{
+	  err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
+	  break;
+	}
+      gcry_sexp_release (sublist);
+      sublist = NULL;
+
+      mpis[i] = sig_value;
+    }
+  if (err)
+    goto out;
 
-  data = NULL;
+  /* DSA specific code.  */
 
   /* FIXME: Why this complicated code?  Why collecting boths mpis in a
      buffer instead of writing them out one after the other?  */
@@ -1312,9 +1506,10 @@ ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
   err = stream_write_string (signature_blob, buffer, sizeof (buffer));
 
  out:
-
   xfree (data);
-
+  gcry_sexp_release (valuelist);
+  gcry_sexp_release (sublist);
+  mpint_list_free (mpis);
   return err;
 }
 
@@ -1322,15 +1517,62 @@ ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
 /* Signature encoder function for ECDSA.  */
 static gpg_error_t
 ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
-                             estream_t stream, gcry_mpi_t *mpis)
+                             estream_t stream, gcry_sexp_t s_signature)
 {
+  gpg_error_t err = 0;
+  gcry_sexp_t valuelist = NULL;
+  gcry_sexp_t sublist = NULL;
+  gcry_mpi_t sig_value = NULL;
+  gcry_mpi_t *mpis = NULL;
+  const char *elems;
+  size_t elems_n;
+  int i;
+
   unsigned char *data[2] = {NULL, NULL};
   size_t data_n[2];
   size_t innerlen;
-  gpg_error_t err;
-  int i;
 
-  (void)spec;
+  valuelist = gcry_sexp_nth (s_signature, 1);
+  if (!valuelist)
+    {
+      err = gpg_error (GPG_ERR_INV_SEXP);
+      goto out;
+    }
+
+  elems = spec->elems_signature;
+  elems_n = strlen (elems);
+
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+  if (!mpis)
+    {
+      err = gpg_error_from_syserror ();
+      goto out;
+    }
+
+  for (i = 0; i < elems_n; i++)
+    {
+      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+      if (!sublist)
+	{
+	  err = gpg_error (GPG_ERR_INV_SEXP);
+	  break;
+	}
+
+      sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+      if (!sig_value)
+	{
+	  err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
+	  break;
+	}
+      gcry_sexp_release (sublist);
+      sublist = NULL;
+
+      mpis[i] = sig_value;
+    }
+  if (err)
+    goto out;
+
+  /* ECDSA specific */
 
   innerlen = 0;
   for (i = 0; i < DIM(data); i++)
@@ -1355,103 +1597,230 @@ ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
  out:
   for (i = 0; i < DIM(data); i++)
     xfree (data[i]);
+  gcry_sexp_release (valuelist);
+  gcry_sexp_release (sublist);
+  mpint_list_free (mpis);
   return err;
 }
 
 
-/*
-   S-Expressions.
- */
-
-
-/* This function constructs a new S-Expression for the key identified
-   by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to
-   be stored at R_SEXP.  Returns an error code.  */
+/* Signature encoder function for EdDSA.  */
 static gpg_error_t
-sexp_key_construct (gcry_sexp_t *r_sexp,
-		    ssh_key_type_spec_t key_spec, int secret,
-		    const char *curve_name, gcry_mpi_t *mpis,
-                    const char *comment)
+ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
+                             estream_t stream, gcry_sexp_t s_signature)
 {
-  const char *key_identifier[] = { "public-key", "private-key" };
-  gpg_error_t err;
-  gcry_sexp_t sexp_new = NULL;
-  void *formatbuf = NULL;
-  void **arg_list = NULL;
-  int arg_idx;
-  estream_t format;
+  gpg_error_t err = 0;
+  gcry_sexp_t valuelist = NULL;
+  gcry_sexp_t sublist = NULL;
+  gcry_mpi_t sig_value = NULL;
+  gcry_mpi_t *mpis = NULL;
   const char *elems;
   size_t elems_n;
-  unsigned int i, j;
+  int i;
 
-  if (secret)
-    elems = key_spec.elems_sexp_order;
-  else
-    elems = key_spec.elems_key_public;
-  elems_n = strlen (elems);
+  unsigned char *data[2] = {NULL, NULL};
+  size_t data_n[2];
+  size_t totallen;
 
-  format = es_fopenmem (0, "a+b");
-  if (!format)
+  valuelist = gcry_sexp_nth (s_signature, 1);
+  if (!valuelist)
     {
-      err = gpg_error_from_syserror ();
+      err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
 
-  /* Key identifier, algorithm identifier, mpis, comment, and a NULL
-     as a safeguard. */
-  arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
-  if (!arg_list)
+  elems = spec->elems_signature;
+  elems_n = strlen (elems);
+
+  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+  if (!mpis)
     {
       err = gpg_error_from_syserror ();
       goto out;
     }
-  arg_idx = 0;
-
-  es_fputs ("(%s(%s", format);
-  arg_list[arg_idx++] = &key_identifier[secret];
-  arg_list[arg_idx++] = &key_spec.identifier;
-  if (curve_name)
-    {
-      es_fputs ("(curve%s)", format);
-      arg_list[arg_idx++] = &curve_name;
-    }
 
   for (i = 0; i < elems_n; i++)
     {
-      es_fprintf (format, "(%c%%m)", elems[i]);
-      if (secret)
+      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+      if (!sublist)
 	{
-	  for (j = 0; j < elems_n; j++)
-	    if (key_spec.elems_key_secret[j] == elems[i])
-	      break;
+	  err = gpg_error (GPG_ERR_INV_SEXP);
+	  break;
 	}
-      else
-	j = i;
-      arg_list[arg_idx++] = &mpis[j];
-    }
-  es_fputs (")(comment%s))", format);
-  arg_list[arg_idx++] = &comment;
-  arg_list[arg_idx] = NULL;
 
-  es_putc (0, format);
-  if (es_ferror (format))
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
+      sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+      if (!sig_value)
+	{
+	  err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
+	  break;
+	}
+      gcry_sexp_release (sublist);
+      sublist = NULL;
+
+      mpis[i] = sig_value;
     }
-  if (es_fclose_snatch (format, &formatbuf, NULL))
+  if (err)
+    goto out;
+
+  /* EdDSA specific.  Actually TOTALLEN will always be 64.  */
+
+  totallen = 0;
+  for (i = 0; i < DIM(data); i++)
     {
-      err = gpg_error_from_syserror ();
-      goto out;
+      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data[i], &data_n[i], mpis[i]);
+      if (err)
+	goto out;
+      totallen += data_n[i];
     }
-  format = NULL;
 
-  err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
+  gcry_log_debug ("  out: len=%zu\n", totallen);
+  err = stream_write_uint32 (stream, totallen);
   if (err)
     goto out;
 
-  *r_sexp = sexp_new;
-  err = 0;
+  for (i = 0; i < DIM(data); i++)
+    {
+      gcry_log_debughex ("  out", data[i], data_n[i]);
+      err = stream_write_data (stream, data[i], data_n[i]);
+      if (err)
+        goto out;
+    }
+
+ out:
+  for (i = 0; i < DIM(data); i++)
+    xfree (data[i]);
+  gcry_sexp_release (valuelist);
+  gcry_sexp_release (sublist);
+  mpint_list_free (mpis);
+  return err;
+}
+
+
+/*
+   S-Expressions.
+ */
+
+
+/* This function constructs a new S-Expression for the key identified
+   by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to
+   be stored at R_SEXP.  Returns an error code.  */
+static gpg_error_t
+sexp_key_construct (gcry_sexp_t *r_sexp,
+		    ssh_key_type_spec_t key_spec, int secret,
+		    const char *curve_name, gcry_mpi_t *mpis,
+                    const char *comment)
+{
+  gpg_error_t err;
+  gcry_sexp_t sexp_new = NULL;
+  void *formatbuf = NULL;
+  void **arg_list = NULL;
+  estream_t format = NULL;
+
+
+  if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
+    {
+      /* It is much easier and more readable to use a separate code
+         path for EdDSA.  */
+      if (!curve_name)
+        err = gpg_error (GPG_ERR_INV_CURVE);
+      else if (!mpis[0] || !gcry_mpi_get_flag (mpis[0], GCRYMPI_FLAG_OPAQUE))
+        err = gpg_error (GPG_ERR_BAD_PUBKEY);
+      else if (secret
+               && (!mpis[1]
+                   || !gcry_mpi_get_flag (mpis[1], GCRYMPI_FLAG_OPAQUE)))
+        err = gpg_error (GPG_ERR_BAD_SECKEY);
+      else if (secret)
+        err = gcry_sexp_build (&sexp_new, NULL,
+                               "(private-key(ecc(curve %s)"
+                               "(flags eddsa)(q %m)(d %m))"
+                               "(comment%s))",
+                               curve_name,
+                               mpis[0], mpis[1],
+                               comment? comment:"");
+      else
+        err = gcry_sexp_build (&sexp_new, NULL,
+                               "(public-key(ecc(curve %s)"
+                               "(flags eddsa)(q %m))"
+                               "(comment%s))",
+                               curve_name,
+                               mpis[0],
+                               comment? comment:"");
+    }
+  else
+    {
+      const char *key_identifier[] = { "public-key", "private-key" };
+      int arg_idx;
+      const char *elems;
+      size_t elems_n;
+      unsigned int i, j;
+
+      if (secret)
+        elems = key_spec.elems_sexp_order;
+      else
+        elems = key_spec.elems_key_public;
+      elems_n = strlen (elems);
+
+      format = es_fopenmem (0, "a+b");
+      if (!format)
+        {
+          err = gpg_error_from_syserror ();
+          goto out;
+        }
+
+      /* Key identifier, algorithm identifier, mpis, comment, and a NULL
+         as a safeguard. */
+      arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
+      if (!arg_list)
+        {
+          err = gpg_error_from_syserror ();
+          goto out;
+        }
+      arg_idx = 0;
+
+      es_fputs ("(%s(%s", format);
+      arg_list[arg_idx++] = &key_identifier[secret];
+      arg_list[arg_idx++] = &key_spec.identifier;
+      if (curve_name)
+        {
+          es_fputs ("(curve%s)", format);
+          arg_list[arg_idx++] = &curve_name;
+        }
+
+      for (i = 0; i < elems_n; i++)
+        {
+          es_fprintf (format, "(%c%%m)", elems[i]);
+          if (secret)
+            {
+              for (j = 0; j < elems_n; j++)
+                if (key_spec.elems_key_secret[j] == elems[i])
+                  break;
+            }
+          else
+            j = i;
+          arg_list[arg_idx++] = &mpis[j];
+        }
+      es_fputs (")(comment%s))", format);
+      arg_list[arg_idx++] = &comment;
+      arg_list[arg_idx] = NULL;
+
+      es_putc (0, format);
+      if (es_ferror (format))
+        {
+          err = gpg_error_from_syserror ();
+          goto out;
+        }
+      if (es_fclose_snatch (format, &formatbuf, NULL))
+        {
+          err = gpg_error_from_syserror ();
+          goto out;
+        }
+      format = NULL;
+
+      err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
+    }
+
+  if (!err)
+    *r_sexp = sexp_new;
 
  out:
   es_fclose (format);
@@ -1462,96 +1831,66 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
 }
 
 
-/* This functions breaks up the key contained in the S-Expression SEXP
-   according to KEY_SPEC.  The MPIs are bundled in a newly create
-   list, which is to be stored in MPIS; a newly allocated string
-   holding the curve name may be stored at RCURVE, and a comment will
-   be stored at COMMENT; SECRET will be filled with a boolean flag
-   specifying what kind of key it is.  Returns an error code.  */
+/* This function extracts the key from the s-expression SEXP according
+   to KEY_SPEC and stores it in ssh format at (R_BLOB, R_BLOBLEN).  If
+   WITH_SECRET is true, the secret key parts are also extracted if
+   possible.  Returns 0 on success or an error code.  Note that data
+   stored at R_BLOB must be freed using es_free!  */
 static gpg_error_t
-sexp_key_extract (gcry_sexp_t sexp,
-		  ssh_key_type_spec_t key_spec, int *secret,
-		  gcry_mpi_t **mpis, char **r_curve, char **comment)
+ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
+                 ssh_key_type_spec_t key_spec,
+                 void **r_blob, size_t *r_blob_size)
 {
   gpg_error_t err = 0;
   gcry_sexp_t value_list = NULL;
   gcry_sexp_t value_pair = NULL;
-  gcry_sexp_t comment_list = NULL;
-  unsigned int i;
-  char *comment_new = NULL;
-  const char *data;
-  size_t data_n;
-  int is_secret;
-  size_t elems_n;
-  const char *elems;
-  gcry_mpi_t *mpis_new = NULL;
-  gcry_mpi_t mpi;
   char *curve_name = NULL;
+  estream_t stream = NULL;
+  void *blob = NULL;
+  size_t blob_size;
+  const char *elems, *p_elems;
+  const char *data;
+  size_t datalen;
 
-  data = gcry_sexp_nth_data (sexp, 0, &data_n);
-  if (! data)
+  *r_blob = NULL;
+  *r_blob_size = 0;
+
+  stream = es_fopenmem (0, "r+b");
+  if (!stream)
     {
-      err = gpg_error (GPG_ERR_INV_SEXP);
+      err = gpg_error_from_syserror ();
       goto out;
     }
 
-  if ((data_n == 10 && !strncmp (data, "public-key", 10))
-      || (data_n == 21 && !strncmp (data, "protected-private-key", 21))
-      || (data_n == 20 && !strncmp (data, "shadowed-private-key", 20)))
-    {
-      is_secret = 0;
-      elems = key_spec.elems_key_public;
-    }
-  else if (data_n == 11 && !strncmp (data, "private-key", 11))
-    {
-      is_secret = 1;
-      elems = key_spec.elems_key_secret;
-    }
-  else
+  /* Get the type of the key extpression.  */
+  data = gcry_sexp_nth_data (sexp, 0, &datalen);
+  if (!data)
     {
       err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
 
-  elems_n = strlen (elems);
-  mpis_new = xtrycalloc (elems_n + 1, sizeof *mpis_new );
-  if (!mpis_new)
+  if ((datalen == 10 && !strncmp (data, "public-key", 10))
+      || (datalen == 21 && !strncmp (data, "protected-private-key", 21))
+      || (datalen == 20 && !strncmp (data, "shadowed-private-key", 20)))
+    elems = key_spec.elems_key_public;
+  else if (datalen == 11 && !strncmp (data, "private-key", 11))
+    elems = with_secret? key_spec.elems_key_secret : key_spec.elems_key_public;
+  else
     {
-      err = gpg_error_from_syserror ();
+      err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
 
+  /* Get the algorithm identifier.  */
   value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
-  if (! value_list)
+  if (!value_list)
     {
       err = gpg_error (GPG_ERR_INV_SEXP);
       goto out;
     }
 
-  for (i = 0; i < elems_n; i++)
-    {
-      value_pair = gcry_sexp_find_token (value_list, elems + i, 1);
-      if (! value_pair)
-	{
-	  err = gpg_error (GPG_ERR_INV_SEXP);
-	  break;
-	}
-
-      /* Note that we need to use STD format; i.e. prepend a 0x00 to
-         indicate a positive number if the high bit is set. */
-      mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
-      if (! mpi)
-	{
-	  err = gpg_error (GPG_ERR_INV_SEXP);
-	  break;
-	}
-      mpis_new[i] = mpi;
-      gcry_sexp_release (value_pair);
-      value_pair = NULL;
-    }
-  if (err)
-    goto out;
-
+  /* Write the ssh algorithm identifier.  */
   if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
     {
       /* Parse the "curve" parameter.  We currently expect the curve
@@ -1559,7 +1898,9 @@ sexp_key_extract (gcry_sexp_t sexp,
          easily be changed but then we need to find the curve name
          from the parameters using gcry_pk_get_curve.  */
       const char *mapped;
+      const char *sshname;
 
+      gcry_sexp_release (value_pair);
       value_pair = gcry_sexp_find_token (value_list, "curve", 5);
       if (!value_pair)
 	{
@@ -1593,48 +1934,87 @@ sexp_key_extract (gcry_sexp_t sexp,
               goto out;
             }
         }
-      gcry_sexp_release (value_pair);
-      value_pair = NULL;
-    }
 
-  /* We do not require a comment sublist to be present here.  */
-  data = NULL;
-  data_n = 0;
+      sshname = ssh_identifier_from_curve_name (curve_name);
+      if (!sshname)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+          goto out;
+        }
+      err = stream_write_cstring (stream, sshname);
+      if (err)
+        goto out;
+      err = stream_write_cstring (stream, curve_name);
+      if (err)
+        goto out;
+    }
+  else
+    {
+      /* Note: This is also used for EdDSA.  */
+      err = stream_write_cstring (stream, key_spec.ssh_identifier);
+      if (err)
+        goto out;
+    }
 
-  comment_list = gcry_sexp_find_token (sexp, "comment", 0);
-  if (comment_list)
-    data = gcry_sexp_nth_data (comment_list, 1, &data_n);
-  if (! data)
+  /* Write the parameters.  */
+  for (p_elems = elems; *p_elems; p_elems++)
     {
-      data = "(none)";
-      data_n = 6;
+      gcry_sexp_release (value_pair);
+      value_pair = gcry_sexp_find_token (value_list, p_elems, 1);
+      if (!value_pair)
+	{
+	  err = gpg_error (GPG_ERR_INV_SEXP);
+	  goto out;
+	}
+      if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
+        {
+
+          data = gcry_sexp_nth_data (value_pair, 1, &datalen);
+          if (!data)
+            {
+              err = gpg_error (GPG_ERR_INV_SEXP);
+              goto out;
+            }
+          err = stream_write_string (stream, data, datalen);
+          if (err)
+            goto out;
+        }
+      else
+        {
+          gcry_mpi_t mpi;
+
+          /* Note that we need to use STD format; i.e. prepend a 0x00
+             to indicate a positive number if the high bit is set. */
+          mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
+          if (!mpi)
+            {
+              err = gpg_error (GPG_ERR_INV_SEXP);
+              goto out;
+            }
+          err = stream_write_mpi (stream, mpi);
+          gcry_mpi_release (mpi);
+          if (err)
+            goto out;
+        }
     }
 
-  comment_new = make_cstring (data, data_n);
-  if (! comment_new)
+  if (es_fclose_snatch (stream, &blob, &blob_size))
     {
       err = gpg_error_from_syserror ();
       goto out;
     }
+  stream = NULL;
 
-  if (secret)
-    *secret = is_secret;
-  *mpis = mpis_new;
-  *comment = comment_new;
-  *r_curve = curve_name;
+  *r_blob = blob;
+  blob = NULL;
+  *r_blob_size = blob_size;
 
  out:
-
   gcry_sexp_release (value_list);
   gcry_sexp_release (value_pair);
-  gcry_sexp_release (comment_list);
-
-  if (err)
-    {
-      xfree (curve_name);
-      xfree (comment_new);
-      mpint_list_free (mpis_new);
-    }
+  xfree (curve_name);
+  es_fclose (stream);
+  es_free (blob);
 
   return err;
 }
@@ -1702,6 +2082,11 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
   gpg_error_t err;
   unsigned int i;
 
+  /* FIXME: Although this sees to work, it not be correct if the
+     lookup is done via name which might be "ecc" but actually it need
+     to check the flags to see whether it is eddsa or ecdsa.  Maybe
+     the entire parameter controlled logic is too complicated and we
+     would do better by just switching on the ssh_name.  */
   for (i = 0; i < DIM (ssh_key_types); i++)
     if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
 	|| (name && (! strcmp (name, ssh_key_types[i].identifier))))
@@ -1719,23 +2104,6 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
 }
 
 
-/* Lookup the ssh-identifier for the ECC curve CURVE_NAME.  Returns
-   NULL if not found.  */
-static const char *
-ssh_identifier_from_curve_name (const char *curve_name)
-{
-  int i;
-
-  for (i = 0; i < DIM (ssh_key_types); i++)
-    if (ssh_key_types[i].curve_name
-        && !strcmp (ssh_key_types[i].curve_name, curve_name))
-      return ssh_key_types[i].ssh_identifier;
-
-  return NULL;
-}
-
-
-
 /* Receive a key from STREAM, according to the key specification given
    as KEY_SPEC.  Depending on SECRET, receive a secret or a public
    key.  If READ_COMMENT is true, receive a comment string as well.
@@ -1763,199 +2131,195 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
   if (err)
     goto out;
 
-  if ((spec.flags & SPEC_FLAG_IS_ECDSA))
+  if ((spec.flags & SPEC_FLAG_IS_EdDSA))
     {
-      /* The format of an ECDSA key is:
-       *   string	key_type ("ecdsa-sha2-nistp256" |
-       *                          "ecdsa-sha2-nistp384" |
-       *		          "ecdsa-sha2-nistp521" )
-       *   string	ecdsa_curve_name
-       *   string	ecdsa_public_key
-       *   mpint	ecdsa_private
+      /* The format of an EdDSA key is:
+       *   string	key_type ("ssh-ed25519")
+       *   string	public_key
+       *   string	private_key
        *
-       * Note that we use the mpint reader instead of the string
-       * reader for ecsa_public_key.
+       * Note that the private key is the concatenation of the private
+       * key with the public key.  Thus theres are 64 bytes; however
+       * we only want the real 32 byte private key - Libgcrypt expects
+       * this.
        */
-      unsigned char *buffer;
-      const char *mapped;
+      mpi_list = xtrycalloc (3, sizeof *mpi_list);
+      if (!mpi_list)
+        {
+          err = gpg_error_from_syserror ();
+          goto out;
+        }
 
-      err = stream_read_string (stream, 0, &buffer, NULL);
+      err = stream_read_blob (stream, 0, &mpi_list[0]);
       if (err)
         goto out;
-      curve_name = buffer;
-      /* Fixme: Check that curve_name matches the keytype.  */
-      /* Because Libgcrypt < 1.6 has no support for the "nistpNNN"
-         curve names, we need to translate them here to Libgcrypt's
-         native names.  */
-      if (!strcmp (curve_name, "nistp256"))
-        mapped = "NIST P-256";
-      else if (!strcmp (curve_name, "nistp384"))
-        mapped = "NIST P-384";
-      else if (!strcmp (curve_name, "nistp521"))
-        mapped = "NIST P-521";
-      else
-        mapped = NULL;
-      if (mapped)
+      if (secret)
         {
-          xfree (curve_name);
-          curve_name = xtrystrdup (mapped);
-          if (!curve_name)
+          u32 len = 0;
+          unsigned char *buffer;
+
+          /* Read string length.  */
+          err = stream_read_uint32 (stream, &len);
+          if (err)
+            goto out;
+          if (len != 32 && len != 64)
+            {
+              err = gpg_error (GPG_ERR_BAD_SECKEY);
+              goto out;
+            }
+          buffer = xtrymalloc_secure (32);
+          if (!buffer)
             {
               err = gpg_error_from_syserror ();
               goto out;
             }
+          err = stream_read_data (stream, buffer, 32);
+          if (err)
+            {
+              xfree (buffer);
+              goto out;
+            }
+          mpi_list[1] = gcry_mpi_set_opaque (NULL, buffer, 8*32);
+          buffer = NULL;
+          if (len == 64)
+            {
+              err = stream_read_skip (stream, 32);
+              if (err)
+                goto out;
+            }
         }
-  }
-
-  err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
-  if (err)
-    goto out;
-
-  if (read_comment)
-    {
-      err = stream_read_cstring (stream, &comment);
-      if (err)
-	goto out;
-    }
-
-  if (secret)
-    elems = spec.elems_key_secret;
-  else
-    elems = spec.elems_key_public;
-
-  if (spec.key_modifier)
-    {
-      err = (*spec.key_modifier) (elems, mpi_list);
-      if (err)
-	goto out;
-    }
-
-  err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
-                            comment? comment:"");
-  if (err)
-    goto out;
-
-  if (key_spec)
-    *key_spec = spec;
-  *key_new = key;
-
- out:
-  mpint_list_free (mpi_list);
-  xfree (curve_name);
-  xfree (key_type);
-  xfree (comment);
-
-  return err;
-}
-
-/* Converts a key of type TYPE, whose key material is given in MPIS,
-   into a newly created binary blob, which is to be stored in
-   BLOB/BLOB_SIZE.  Returns zero on success or an error code.  */
-static gpg_error_t
-ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
-			 ssh_key_type_spec_t *spec,
-                         const char *curve_name, gcry_mpi_t *mpis)
-{
-  unsigned char *blob_new;
-  long int blob_size_new;
-  estream_t stream;
-  gpg_error_t err;
-  unsigned int i;
-
-  *blob = NULL;
-  *blob_size = 0;
-
-  blob_new = NULL;
-  stream = NULL;
-  err = 0;
-
-  stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
-  if (! stream)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
     }
-
-  if ((spec->flags & SPEC_FLAG_IS_ECDSA) && curve_name)
+  else if ((spec.flags & SPEC_FLAG_IS_ECDSA))
     {
-      const char *sshname = ssh_identifier_from_curve_name (curve_name);
-      if (!curve_name)
-        {
-          err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
-          goto out;
-        }
-      err = stream_write_cstring (stream, sshname);
+      /* The format of an ECDSA key is:
+       *   string	key_type ("ecdsa-sha2-nistp256" |
+       *                          "ecdsa-sha2-nistp384" |
+       *		          "ecdsa-sha2-nistp521" )
+       *   string	ecdsa_curve_name
+       *   string	ecdsa_public_key
+       *   mpint	ecdsa_private
+       *
+       * Note that we use the mpint reader instead of the string
+       * reader for ecsa_public_key.
+       */
+      unsigned char *buffer;
+      const char *mapped;
+
+      err = stream_read_string (stream, 0, &buffer, NULL);
       if (err)
         goto out;
-      err = stream_write_cstring (stream, curve_name);
+      curve_name = buffer;
+      /* Fixme: Check that curve_name matches the keytype.  */
+      /* Because Libgcrypt < 1.6 has no support for the "nistpNNN"
+         curve names, we need to translate them here to Libgcrypt's
+         native names.  */
+      if (!strcmp (curve_name, "nistp256"))
+        mapped = "NIST P-256";
+      else if (!strcmp (curve_name, "nistp384"))
+        mapped = "NIST P-384";
+      else if (!strcmp (curve_name, "nistp521"))
+        mapped = "NIST P-521";
+      else
+        mapped = NULL;
+      if (mapped)
+        {
+          xfree (curve_name);
+          curve_name = xtrystrdup (mapped);
+          if (!curve_name)
+            {
+              err = gpg_error_from_syserror ();
+              goto out;
+            }
+        }
+
+      err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
       if (err)
         goto out;
     }
   else
     {
-      err = stream_write_cstring (stream, spec->ssh_identifier);
+      err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
       if (err)
         goto out;
     }
 
-  for (i = 0; mpis[i]; i++)
-    if ((err = stream_write_mpi (stream, mpis[i])))
-      goto out;
-
-  blob_size_new = es_ftell (stream);
-  if (blob_size_new == -1)
+  if (read_comment)
     {
-      err = gpg_error_from_syserror ();
-      goto out;
+      err = stream_read_cstring (stream, &comment);
+      if (err)
+	goto out;
     }
 
-  err = es_fseek (stream, 0, SEEK_SET);
-  if (err)
-    goto out;
+  if (secret)
+    elems = spec.elems_key_secret;
+  else
+    elems = spec.elems_key_public;
 
-  blob_new = xtrymalloc (blob_size_new);
-  if (! blob_new)
+  if (spec.key_modifier)
     {
-      err = gpg_error_from_syserror ();
-      goto out;
+      err = (*spec.key_modifier) (elems, mpi_list);
+      if (err)
+	goto out;
     }
 
-  err = stream_read_data (stream, blob_new, blob_size_new);
-  if (err)
-    goto out;
+  if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+    {
+      if (secret)
+        {
+          err = gcry_sexp_build (&key, NULL,
+                                 "(private-key(ecc(curve \"Ed25519\")"
+                                 "(flags eddsa)(q %m)(d %m))"
+                                 "(comment%s))",
+                                 mpi_list[0], mpi_list[1],
+                                 comment? comment:"");
+        }
+      else
+        {
+          err = gcry_sexp_build (&key, NULL,
+                                 "(public-key(ecc(curve \"Ed25519\")"
+                                 "(flags eddsa)(q %m))"
+                                 "(comment%s))",
+                                 mpi_list[0],
+                                 comment? comment:"");
+        }
+    }
+  else
+    {
+      err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
+                                comment? comment:"");
+      if (err)
+        goto out;
+    }
 
-  *blob = blob_new;
-  *blob_size = blob_size_new;
+  if (key_spec)
+    *key_spec = spec;
+  *key_new = key;
 
  out:
-
-  if (stream)
-    es_fclose (stream);
-  if (err)
-    xfree (blob_new);
+  mpint_list_free (mpi_list);
+  xfree (curve_name);
+  xfree (key_type);
+  xfree (comment);
 
   return err;
 }
 
 
-/* Write the public key KEY_PUBLIC to STREAM in SSH key format.  If
+/* Write the public key from KEY to STREAM in SSH key format.  If
    OVERRIDE_COMMENT is not NULL, it will be used instead of the
    comment stored in the key.  */
 static gpg_error_t
-ssh_send_key_public (estream_t stream,
-                     gcry_sexp_t key_public,
+ssh_send_key_public (estream_t stream, gcry_sexp_t key,
                      const char *override_comment)
 {
   ssh_key_type_spec_t spec;
-  gcry_mpi_t *mpi_list = NULL;
   char *key_type = NULL;
-  char *curve;
   char *comment = NULL;
-  unsigned char *blob = NULL;
-  size_t blob_n;
+  void *blob = NULL;
+  size_t bloblen;
   gpg_error_t err;
 
-  err = sexp_extract_identifier (key_public, &key_type);
+  err = sexp_extract_identifier (key, &key_type);
   if (err)
     goto out;
 
@@ -1963,32 +2327,34 @@ ssh_send_key_public (estream_t stream,
   if (err)
     goto out;
 
-  err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &curve, &comment);
+  err = ssh_key_to_blob (key, 0, spec, &blob, &bloblen);
   if (err)
     goto out;
 
-  err = ssh_convert_key_to_blob (&blob, &blob_n, &spec, curve, mpi_list);
+  err = stream_write_string (stream, blob, bloblen);
   if (err)
     goto out;
 
-  err = stream_write_string (stream, blob, blob_n);
+  if (override_comment)
+    err = stream_write_cstring (stream, override_comment);
+  else
+    {
+      err = ssh_key_extract_comment (key, &comment);
+      if (!err)
+        err = stream_write_cstring (stream, comment);
+    }
   if (err)
     goto out;
 
-  err = stream_write_cstring (stream,
-                              override_comment? override_comment : comment);
-
  out:
-
-  mpint_list_free (mpi_list);
-  xfree (curve);
-  xfree (comment);
   xfree (key_type);
-  xfree (blob);
+  xfree (comment);
+  es_free (blob);
 
   return err;
 }
 
+
 /* Read a public key out of BLOB/BLOB_SIZE according to the key
    specification given as KEY_SPEC, storing the new key in KEY_PUBLIC.
    Returns zero on success or an error code.  */
@@ -2001,7 +2367,7 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
   gpg_error_t err;
 
   err = 0;
-
+  /* FIXME: Use fopenmem_init */
   blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
   if (! blob_stream)
     {
@@ -2045,39 +2411,6 @@ ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
 }
 
 
-/* Converts the secret key KEY_SECRET into a public key, storing it in
-   KEY_PUBLIC.  SPEC is the according key specification.  Returns zero
-   on success or an error code.  */
-static gpg_error_t
-key_secret_to_public (gcry_sexp_t *key_public,
-		      ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
-{
-  char *curve;
-  char *comment;
-  gcry_mpi_t *mpis;
-  gpg_error_t err;
-  int is_secret;
-
-  comment = NULL;
-  mpis = NULL;
-
-  err = sexp_key_extract (key_secret, spec, &is_secret, &mpis,
-                          &curve, &comment);
-  if (err)
-    goto out;
-
-  err = sexp_key_construct (key_public, spec, 0, curve, mpis, comment);
-
- out:
-
-  mpint_list_free (mpis);
-  xfree (comment);
-  xfree (curve);
-
-  return err;
-}
-
-
 /* Check whether a smartcard is available and whether it has a usable
    key.  Store a copy of that key at R_PK and return 0.  If no key is
    available store NULL at R_PK and return an error code.  If CARDSN
@@ -2376,20 +2709,12 @@ ssh_handler_request_identities (ctrl_t ctrl,
           goto out;
       }
 
-      err = key_secret_to_public (&key_public, spec, key_secret);
+      err = ssh_send_key_public (key_blobs, key_secret, NULL);
       if (err)
         goto out;
-
       gcry_sexp_release (key_secret);
       key_secret = NULL;
 
-      err = ssh_send_key_public (key_blobs, key_public, NULL);
-      if (err)
-        goto out;
-
-      gcry_sexp_release (key_public);
-      key_public = NULL;
-
       key_counter++;
     }
   err = 0;
@@ -2441,30 +2766,27 @@ data_hash (unsigned char *data, size_t data_n,
   return 0;
 }
 
-/* This function signs the data contained in CTRL, stores the created
-   signature in newly allocated memory in SIG and it's size in SIG_N;
-   SIG_ENCODER is the signature encoder to use.  */
+
+/* This function signs the data described by CTRL. If HASH is is not
+   NULL, (HASH,HASHLEN) overrides the hash stored in CTRL.  This is to
+   allow the use of signature algorithms that implement the hashing
+   internally (e.g. Ed25519).  On success the created signature is
+   stored in ssh format at R_SIG and it's size at R_SIGLEN; the caller
+   must use es_free to releaase this memory.  */
 static gpg_error_t
 data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
-	   unsigned char **sig, size_t *sig_n)
+           const void *hash, size_t hashlen,
+	   unsigned char **r_sig, size_t *r_siglen)
 {
   gpg_error_t err;
   gcry_sexp_t signature_sexp = NULL;
   estream_t stream = NULL;
-  gcry_sexp_t valuelist = NULL;
-  gcry_sexp_t sublist = NULL;
-  gcry_mpi_t sig_value = NULL;
-  unsigned char *sig_blob = NULL;
-  size_t sig_blob_n = 0;
-  int ret;
-  unsigned int i;
-  const char *elems;
-  size_t elems_n;
-  gcry_mpi_t *mpis = NULL;
+  void *blob = NULL;
+  size_t bloblen;
   char hexgrip[40+1];
 
-  *sig = NULL;
-  *sig_n = 0;
+  *r_sig = NULL;
+  *r_siglen = 0;
 
   /* Quick check to see whether we have a valid keygrip and convert it
      to hex.  */
@@ -2516,20 +2838,13 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
                            "for the ssh key%%0A  %F%%0A  (%c)"),
                          &signature_sexp,
                          CACHE_MODE_SSH, ttl_from_sshcontrol,
-                         NULL, 0);
+                         hash, hashlen);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
 
-  valuelist = gcry_sexp_nth (signature_sexp, 1);
-  if (! valuelist)
-    {
-      err = gpg_error (GPG_ERR_INV_SEXP);
-      goto out;
-    }
-
-  stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
-  if (! stream)
+  stream = es_fopenmem (0, "r+b");
+  if (!stream)
     {
       err = gpg_error_from_syserror ();
       goto out;
@@ -2539,99 +2854,40 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
   if (err)
     goto out;
 
-  elems = spec->elems_signature;
-  elems_n = strlen (elems);
-
-  mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
-  if (!mpis)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
-
-  for (i = 0; i < elems_n; i++)
-    {
-      sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
-      if (! sublist)
-	{
-	  err = gpg_error (GPG_ERR_INV_SEXP);
-	  break;
-	}
-
-      sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
-      if (! sig_value)
-	{
-	  err = gpg_error (GPG_ERR_INTERNAL); /* FIXME?  */
-	  break;
-	}
-      gcry_sexp_release (sublist);
-      sublist = NULL;
-
-      mpis[i] = sig_value;
-    }
+  err = spec->signature_encoder (spec, stream, signature_sexp);
   if (err)
     goto out;
 
-  err = spec->signature_encoder (spec, stream, mpis);
-  if (err)
-    goto out;
-
-  sig_blob_n = es_ftell (stream);
-  if (sig_blob_n == -1)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
-
-  sig_blob = xtrymalloc (sig_blob_n);
-  if (! sig_blob)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
-
-  ret = es_fseek (stream, 0, SEEK_SET);
-  if (ret)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
-
-  err = stream_read_data (stream, sig_blob, sig_blob_n);
+  err = es_fclose_snatch (stream, &blob, &bloblen);
   if (err)
     goto out;
+  stream = NULL;
 
-  *sig = sig_blob;
-  *sig_n = sig_blob_n;
+  *r_sig = blob; blob = NULL;
+  *r_siglen = bloblen;
 
  out:
-
-  if (err)
-    xfree (sig_blob);
-
-  if (stream)
-    es_fclose (stream);
-  gcry_sexp_release (valuelist);
+  xfree (blob);
+  es_fclose (stream);
   gcry_sexp_release (signature_sexp);
-  gcry_sexp_release (sublist);
-  mpint_list_free (mpis);
 
   return err;
 }
 
+
 /* Handler for the "sign_request" command.  */
 static gpg_error_t
 ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
 {
-  gcry_sexp_t key;
+  gcry_sexp_t key = NULL;
   ssh_key_type_spec_t spec;
   unsigned char hash[MAX_DIGEST_LEN];
   unsigned int hash_n;
   unsigned char key_grip[20];
-  unsigned char *key_blob;
+  unsigned char *key_blob = NULL;
   u32 key_blob_size;
-  unsigned char *data;
-  unsigned char *sig;
+  unsigned char *data = NULL;
+  unsigned char *sig = NULL;
   size_t sig_n;
   u32 data_size;
   u32 flags;
@@ -2639,11 +2895,6 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   gpg_error_t ret_err;
   int hash_algo;
 
-  key_blob = NULL;
-  data = NULL;
-  sig = NULL;
-  key = NULL;
-
   /* Receive key.  */
 
   err = stream_read_string (request, 0, &key_blob, &key_blob_size);
@@ -2667,42 +2918,48 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   hash_algo = spec.hash_algo;
   if (!hash_algo)
     hash_algo = GCRY_MD_SHA1;  /* Use the default.  */
-
-  /* Hash data.  */
-  hash_n = gcry_md_get_algo_dlen (hash_algo);
-  if (! hash_n)
-    {
-      err = gpg_error (GPG_ERR_INTERNAL);
-      goto out;
-    }
-  err = data_hash (data, data_size, hash_algo, hash);
-  if (err)
-    goto out;
-
-  /* Calculate key grip.  */
-  err = ssh_key_grip (key, key_grip);
-  if (err)
-    goto out;
-
-  /* Sign data.  */
-
   ctrl->digest.algo = hash_algo;
-  memcpy (ctrl->digest.value, hash, hash_n);
-  ctrl->digest.valuelen = hash_n;
   if ((spec.flags & SPEC_FLAG_USE_PKCS1V2))
     ctrl->digest.raw_value = 0;
   else
     ctrl->digest.raw_value = 1;
+
+  /* Calculate key grip.  */
+  err = ssh_key_grip (key, key_grip);
+  if (err)
+    goto out;
   ctrl->have_keygrip = 1;
   memcpy (ctrl->keygrip, key_grip, 20);
 
-  err = data_sign (ctrl, &spec, &sig, &sig_n);
+  /* Hash data unless we use EdDSA.  */
+  if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+    {
+      ctrl->digest.valuelen = 0;
+    }
+  else
+    {
+      hash_n = gcry_md_get_algo_dlen (hash_algo);
+      if (!hash_n)
+        {
+          err = gpg_error (GPG_ERR_INTERNAL);
+          goto out;
+        }
+      err = data_hash (data, data_size, hash_algo, hash);
+      if (err)
+        goto out;
+      memcpy (ctrl->digest.value, hash, hash_n);
+      ctrl->digest.valuelen = hash_n;
+    }
 
- out:
+  /* Sign data.  */
+  if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+    err = data_sign (ctrl, &spec, data, data_size, &sig, &sig_n);
+  else
+    err = data_sign (ctrl, &spec, NULL, 0, &sig, &sig_n);
 
+ out:
   /* Done.  */
-
-  if (! err)
+  if (!err)
     {
       ret_err = stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
       if (ret_err)
@@ -2713,6 +2970,8 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
     }
   else
     {
+      log_error ("ssh sign request failed: %s <%s>\n",
+                 gpg_strerror (err), gpg_strsource (err));
       ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
       if (ret_err)
 	goto leave;
@@ -2723,54 +2982,35 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
   gcry_sexp_release (key);
   xfree (key_blob);
   xfree (data);
-  xfree (sig);
+  es_free (sig);
 
   return ret_err;
 }
 
+
 /* This function extracts the comment contained in the key
-   S-Expression KEY and stores a copy in COMMENT.  Returns usual error
+   s-expression KEY and stores a copy in COMMENT.  Returns usual error
    code.  */
 static gpg_error_t
-ssh_key_extract_comment (gcry_sexp_t key, char **comment)
+ssh_key_extract_comment (gcry_sexp_t key, char **r_comment)
 {
   gcry_sexp_t comment_list;
-  char *comment_new;
-  const char *data;
-  size_t data_n;
-  gpg_error_t err;
-
-  comment_list = gcry_sexp_find_token (key, "comment", 0);
-  if (! comment_list)
-    {
-      err = gpg_error (GPG_ERR_INV_SEXP);
-      goto out;
-    }
-
-  data = gcry_sexp_nth_data (comment_list, 1, &data_n);
-  if (! data)
-    {
-      err = gpg_error (GPG_ERR_INV_SEXP);
-      goto out;
-    }
-
-  comment_new = make_cstring (data, data_n);
-  if (! comment_new)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
 
-  *comment = comment_new;
-  err = 0;
+  *r_comment = NULL;
 
- out:
+  comment_list = gcry_sexp_find_token (key, "comment", 0);
+  if (!comment_list)
+    return gpg_error (GPG_ERR_INV_SEXP);
 
+  *r_comment = gcry_sexp_nth_string (comment_list, 1);
   gcry_sexp_release (comment_list);
+  if (!*r_comment)
+    return gpg_error (GPG_ERR_INV_SEXP);
 
-  return err;
+  return 0;
 }
 
+
 /* This function converts the key contained in the S-Expression KEY
    into a buffer, which is protected by the passphrase PASSPHRASE.
    Returns usual error code.  */

commit a77ed0f266d03e234027dda4de5a7f3dd6787b1e
Author: Werner Koch <wk at gnupg.org>
Date:   Sat Mar 22 20:51:16 2014 +0100

    agent: Cleanups to prepare implementation of Ed25519.
    
    * agent/cvt-openpgp.c: Remove.
    (convert_to_openpgp): Use gcry_sexp_extract_param.
    * agent/findkey.c (is_eddsa): New.
    (agent_is_dsa_key, agent_is_eddsa_key): Check whether ecc means EdDSA.
    * agent/pksign.c (agent_pksign_do): Add args OVERRIDEDATA and
    OVERRIDEDATALEN.
    
    * common/ssh-utils.c (is_eddsa): New.
    (get_fingerprint): Take care or EdDSA.

diff --git a/agent/agent.h b/agent/agent.h
index eac7ba5..58e5841 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -368,7 +368,8 @@ char *agent_get_cache (const char *key, cache_mode_t cache_mode);
 int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                      const char *desc_text,
 		     gcry_sexp_t *signature_sexp,
-                     cache_mode_t cache_mode, lookup_ttl_t lookup_ttl);
+                     cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+                     const void *overridedata, size_t overridedatalen);
 int agent_pksign (ctrl_t ctrl, const char *cache_nonce,
                   const char *desc_text,
                   membuf_t *outbuf, cache_mode_t cache_mode);
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 4191d6f..fb3b29a 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -2515,7 +2515,8 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
                          _("Please enter the passphrase "
                            "for the ssh key%%0A  %F%%0A  (%c)"),
                          &signature_sexp,
-                         CACHE_MODE_SSH, ttl_from_sshcontrol);
+                         CACHE_MODE_SSH, ttl_from_sshcontrol,
+                         NULL, 0);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
diff --git a/agent/command.c b/agent/command.c
index d1e53cd..fab27f0 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -2147,7 +2147,7 @@ cmd_export_key (assuan_context_t ctx, char *line)
 
   if (!ctrl->server_local->export_key)
     {
-      err = gpg_error (GPG_ERR_MISSING_KEY);
+      err = set_error (GPG_ERR_MISSING_KEY, "did you run KEYWRAP_KEY");
       goto leave;
     }
 
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index 205b953..5718bd9 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1030,46 +1030,6 @@ convert_from_openpgp_native (ctrl_t ctrl,
 }
 
 
-

-static gpg_error_t
-key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
-{
-  gpg_error_t err = 0;
-  gcry_sexp_t l2;
-  int idx;
-
-  for (idx=0; *elems; elems++, idx++)
-    {
-      l2 = gcry_sexp_find_token (sexp, elems, 1);
-      if (!l2)
-        {
-          err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found.  */
-          goto leave;
-        }
-      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      if (!array[idx])
-        {
-          err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid.  */
-          goto leave;
-        }
-    }
-
- leave:
-  if (err)
-    {
-      int i;
-
-      for (i=0; i < idx; i++)
-        {
-          gcry_mpi_release (array[i]);
-          array[i] = NULL;
-        }
-    }
-  return err;
-}
-
-
 /* Given an ARRAY of mpis with the key parameters, protect the secret
    parameters in that array and replace them by one opaque encoded
    mpi.  NPKEY is the number of public key parameters and NSKEY is
@@ -1173,7 +1133,6 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   gpg_error_t err;
   gcry_sexp_t list, l2;
   char *name;
-  int algo;
   const char *algoname;
   const char *elems;
   int npkey, nskey;
@@ -1203,26 +1162,63 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
     }
 
-  algo = gcry_pk_map_name (name);
-  xfree (name);
-
-  switch (algo)
+  /* Map NAME to a name as used by Libgcrypt.  We do not use the
+     Libgcrypt function here because we need a lowercase name and
+     require special treatment for some algorithms.  */
+  strlwr (name);
+  if (!strcmp (name, "rsa"))
+    {
+      algoname = "rsa";
+      npkey = 2;
+      elems = "nedpqu";
+    }
+  else if (!strcmp (name, "elg"))
+    {
+      algoname = "elg";
+      npkey = 3;
+      elems = "pgyx";
+    }
+  else if (!strcmp (name, "dsa"))
+    {
+      algoname = "dsa";
+      npkey = 4;
+      elems = "pqgyx";
+    }
+  else if (!strcmp (name, "ecc"))
+    {
+      algoname = "?"; /* Decide later by checking the usage.  */
+      npkey = 6;
+      elems = "pabgnqd";
+    }
+  else if (!strcmp (name, "ecdsa"))
+    {
+      algoname = "ecdsa";
+      npkey = 6;
+      elems = "pabgnqd";
+    }
+  else if (!strcmp (name, "ecdh"))
     {
-    case GCRY_PK_RSA:   algoname = "rsa";   npkey = 2; elems = "nedpqu";  break;
-    case GCRY_PK_ELG:   algoname = "elg";   npkey = 3; elems = "pgyx";    break;
-    case GCRY_PK_ELG_E: algoname = "elg";   npkey = 3; elems = "pgyx";    break;
-    case GCRY_PK_DSA:   algoname = "dsa";   npkey = 4; elems = "pqgyx";   break;
-    case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break;
-    case GCRY_PK_ECDH:  algoname = "ecdh";  npkey = 6; elems = "pabgnqd"; break;
-    default:            algoname = "";      npkey = 0; elems = NULL;      break;
+      algoname = "ecdh";
+      npkey = 6;
+      elems = "pabgnqd";
     }
+  else
+    {
+      algoname = "";
+      npkey = 0;
+      elems = NULL;
+    }
+  xfree (name);
   assert (!elems || strlen (elems) < DIM (array) );
   nskey = elems? strlen (elems) : 0;
 
+  /* Extract the parameters and put them into an array.  */
   if (!elems)
     err = gpg_error (GPG_ERR_PUBKEY_ALGO);
   else
-    err = key_from_sexp (list, elems, array);
+    err = gcry_sexp_extract_param (list, NULL, elems,
+                                   array+0, array+1, array+2, array+3, array+4,
+                                   array+5, array+6, NULL);
   gcry_sexp_release (list);
   if (err)
     return err;
diff --git a/agent/findkey.c b/agent/findkey.c
index 7b24c55..84d2cfd 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -688,7 +688,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
    string describing the names of the parameters.  ALGONAMESIZE and
    ELEMSSIZE give the allocated size of the provided buffers.  The
    buffers may be NULL if not required.  If R_LIST is not NULL the top
-   level list will be stored tehre; the caller needs to release it in
+   level list will be stored there; the caller needs to release it in
    this case.  */
 static gpg_error_t
 key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
@@ -776,27 +776,65 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
 }
 
 
+/* Return true if KEYPARMS holds an EdDSA key.  */
+static int
+is_eddsa (gcry_sexp_t keyparms)
+{
+  int result = 0;
+  gcry_sexp_t list;
+  const char *s;
+  size_t n;
+  int i;
+
+  list = gcry_sexp_find_token (keyparms, "flags", 0);
+  for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
+    {
+      s = gcry_sexp_nth_data (list, i, &n);
+      if (!s)
+        continue; /* Not a data element. */
+
+      if (n == 5 && !memcmp (s, "eddsa", 5))
+        {
+          result = 1;
+          break;
+        }
+    }
+  gcry_sexp_release (list);
+  return result;
+}
+
+
 /* Return the public key algorithm number if S_KEY is a DSA style key.
    If it is not a DSA style key, return 0.  */
 int
 agent_is_dsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an DSA key.  */
 
   if (!strcmp (algoname, "dsa"))
-    return GCRY_PK_DSA;
+    result = GCRY_PK_DSA;
   else if (!strcmp (algoname, "ecc"))
-    return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag.  */
+    {
+      if (is_eddsa (list))
+        result = 0;
+      else
+        result = GCRY_PK_ECDSA;
+    }
   else if (!strcmp (algoname, "ecdsa"))
-    return GCRY_PK_ECDSA;
+    result = GCRY_PK_ECDSA;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
@@ -804,18 +842,25 @@ agent_is_dsa_key (gcry_sexp_t s_key)
 int
 agent_is_eddsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an EdDSA key.  */
 
-  if (!strcmp (algoname, "eddsa"))
-    return 1;
+  if (!strcmp (algoname, "ecc") && is_eddsa (list))
+    result = 1;
+  else if (!strcmp (algoname, "eddsa")) /* backward compatibility.  */
+    result = 1;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
diff --git a/agent/pksign.c b/agent/pksign.c
index 0886150..fb593a6 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -276,18 +276,35 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits,
    the signature S-expression.  LOOKUP is an optional function to
    provide a way for lower layers to ask for the caching TTL.  If a
    CACHE_NONCE is given that cache item is first tried to get a
-   passphrase.  */
+   passphrase.  If OVERRIDEDATA is not NULL, OVERRIDEDATALEN bytes
+   from this buffer are used instead of the data in CTRL.  The
+   override feature is required to allow the use of Ed25519 with ssh
+   because Ed25519 dies the hashing itself.  */
 int
 agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                  const char *desc_text,
 		 gcry_sexp_t *signature_sexp,
-                 cache_mode_t cache_mode, lookup_ttl_t lookup_ttl)
+                 cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+                 const void *overridedata, size_t overridedatalen)
 {
   gcry_sexp_t s_skey = NULL, s_sig = NULL;
   unsigned char *shadow_info = NULL;
   unsigned int rc = 0;		/* FIXME: gpg-error? */
+  const unsigned char *data;
+  int datalen;
 
-  if (! ctrl->have_keygrip)
+  if (overridedata)
+    {
+      data = overridedata;
+      datalen = overridedatalen;
+    }
+  else
+    {
+      data = ctrl->digest.value;
+      datalen = ctrl->digest.valuelen;
+    }
+
+  if (!ctrl->have_keygrip)
     return gpg_error (GPG_ERR_NO_SECKEY);
 
   rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip,
@@ -315,8 +332,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         is_ECDSA = 1;
 
       rc = divert_pksign (ctrl,
-                          ctrl->digest.value,
-                          ctrl->digest.valuelen,
+                          data, datalen,
                           ctrl->digest.algo,
                           shadow_info, &buf, &len);
       if (rc)
@@ -405,22 +421,18 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
       /* Put the hash into a sexp */
       if (agent_is_eddsa_key (s_skey))
-        rc = do_encode_eddsa (ctrl->digest.value,
-                              ctrl->digest.valuelen,
+        rc = do_encode_eddsa (data, datalen,
                               &s_hash);
       else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
-        rc = do_encode_raw_pkcs1 (ctrl->digest.value,
-                                  ctrl->digest.valuelen,
+        rc = do_encode_raw_pkcs1 (data, datalen,
                                   gcry_pk_get_nbits (s_skey),
                                   &s_hash);
       else if ( (dsaalgo = agent_is_dsa_key (s_skey)) )
-        rc = do_encode_dsa (ctrl->digest.value,
-                            ctrl->digest.valuelen,
+        rc = do_encode_dsa (data, datalen,
                             dsaalgo, s_skey,
                             &s_hash);
       else
-        rc = do_encode_md (ctrl->digest.value,
-                           ctrl->digest.valuelen,
+        rc = do_encode_md (data, datalen,
                            ctrl->digest.algo,
                            &s_hash,
                            ctrl->digest.raw_value);
@@ -468,7 +480,8 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
   size_t len = 0;
   int rc = 0;
 
-  rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL);
+  rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL,
+                        NULL, 0);
   if (rc)
     goto leave;
 
diff --git a/common/ssh-utils.c b/common/ssh-utils.c
index 0c71567..a75b3c0 100644
--- a/common/ssh-utils.c
+++ b/common/ssh-utils.c
@@ -37,6 +37,33 @@
 #include "ssh-utils.h"
 
 
+/* Return true if KEYPARMS holds an EdDSA key.  */
+static int
+is_eddsa (gcry_sexp_t keyparms)
+{
+  int result = 0;
+  gcry_sexp_t list;
+  const char *s;
+  size_t n;
+  int i;
+
+  list = gcry_sexp_find_token (keyparms, "flags", 0);
+  for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
+    {
+      s = gcry_sexp_nth_data (list, i, &n);
+      if (!s)
+        continue; /* Not a data element. */
+
+      if (n == 5 && !memcmp (s, "eddsa", 5))
+        {
+          result = 1;
+          break;
+        }
+    }
+  gcry_sexp_release (list);
+  return result;
+}
+
 
 /* Return the Secure Shell type fingerprint for KEY.  The length of
    the fingerprint is returned at R_LEN and the fingerprint itself at
@@ -53,6 +80,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
   int idx;
   const char *elems;
   gcry_md_hd_t md = NULL;
+  int blobmode = 0;
 
   *r_fpr = NULL;
   *r_len = 0;
@@ -93,38 +121,52 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
       elems = "en";
       gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
       break;
+
     case GCRY_PK_DSA:
       elems = "pqgy";
       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
       break;
-    case GCRY_PK_ECDSA:
-      /* We only support the 3 standard curves for now.  It is just a
-         quick hack.  */
-      elems = "q";
-      gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
-      l2 = gcry_sexp_find_token (list, "curve", 0);
-      if (!l2)
-        elems = "";
+
+    case GCRY_PK_ECC:
+      if (is_eddsa (list))
+        {
+          elems = "q";
+          blobmode = 1;
+          /* For now there is just one curve, thus no need to switch
+             on it.  */
+          gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15);
+        }
       else
         {
-          gcry_free (name);
-          name = gcry_sexp_nth_string (l2, 1);
-          gcry_sexp_release (l2);
-          l2 = NULL;
-          if (!name)
+          /* We only support the 3 standard curves for now.  It is
+             just a quick hack.  */
+          elems = "q";
+          gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
+          l2 = gcry_sexp_find_token (list, "curve", 0);
+          if (!l2)
             elems = "";
-          else if (!strcmp (name, "NIST P-256") || !strcmp (name, "nistp256"))
-            gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
-          else if (!strcmp (name, "NIST P-384") || !strcmp (name, "nistp384"))
-            gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
-          else if (!strcmp (name, "NIST P-521") || !strcmp (name, "nistp521"))
-            gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
           else
-            elems = "";
+            {
+              gcry_free (name);
+              name = gcry_sexp_nth_string (l2, 1);
+              gcry_sexp_release (l2);
+              l2 = NULL;
+              if (!name)
+                elems = "";
+              else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256"))
+                gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
+              else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384"))
+                gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
+              else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521"))
+                gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
+              else
+                elems = "";
+            }
+          if (!*elems)
+            err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
         }
-      if (!*elems)
-        err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
       break;
+
     default:
       elems = "";
       err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
@@ -133,33 +175,56 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
   if (err)
     goto leave;
 
+
   for (idx = 0, s = elems; *s; s++, idx++)
     {
-      gcry_mpi_t a;
-      unsigned char *buf;
-      size_t buflen;
-
       l2 = gcry_sexp_find_token (list, s, 1);
       if (!l2)
         {
           err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
           goto leave;
         }
-      a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      l2 = NULL;
-      if (!a)
+      if (blobmode)
         {
-          err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
-          goto leave;
+          const char *blob;
+          size_t bloblen;
+          unsigned char lenbuf[4];
+
+          blob = gcry_sexp_nth_data (l2, 1, &bloblen);
+          if (!blob)
+            {
+              err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
+              goto leave;
+            }
+          lenbuf[0] = bloblen >> 24;
+          lenbuf[1] = bloblen >> 16;
+          lenbuf[2] = bloblen >>  8;
+          lenbuf[3] = bloblen;
+          gcry_md_write (md, lenbuf, 4);
+          gcry_md_write (md, blob, bloblen);
         }
+      else
+        {
+          gcry_mpi_t a;
+          unsigned char *buf;
+          size_t buflen;
 
-      err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
-      gcry_mpi_release (a);
-      if (err)
-        goto leave;
-      gcry_md_write (md, buf, buflen);
-      gcry_free (buf);
+          a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+          gcry_sexp_release (l2);
+          l2 = NULL;
+          if (!a)
+            {
+              err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
+              goto leave;
+            }
+
+          err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
+          gcry_mpi_release (a);
+          if (err)
+            goto leave;
+          gcry_md_write (md, buf, buflen);
+          gcry_free (buf);
+        }
     }
 
   *r_fpr = gcry_malloc (as_string? 61:20);

commit 6376227a31b3076321ce16ad626b333057bda53d
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Mar 18 16:49:38 2014 +0100

    tools: Fix NULL deref in gpg-connect-agent.
    
    * tools/gpg-connect-agent.c (handle_inquire): Do not pass NULL to
    strlen.

diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c
index 42e315c..81e981b 100644
--- a/tools/gpg-connect-agent.c
+++ b/tools/gpg-connect-agent.c
@@ -1936,7 +1936,10 @@ handle_inquire (assuan_context_t ctx, char *line)
   if (d->is_var)
     {
       char *tmpvalue = get_var_ext (d->file);
-      rc = assuan_send_data (ctx, tmpvalue, strlen (tmpvalue));
+      if (tmpvalue)
+        rc = assuan_send_data (ctx, tmpvalue, strlen (tmpvalue));
+      else
+        rc = assuan_send_data (ctx, "", 0);
       xfree (tmpvalue);
       if (rc)
         log_error ("sending data back failed: %s\n", gpg_strerror (rc) );

-----------------------------------------------------------------------

Summary of changes:
 agent/agent.h             |    3 +-
 agent/command-ssh.c       | 1365 ++++++++++++++++++++++++++-------------------
 agent/command.c           |    2 +-
 agent/cvt-openpgp.c       |  102 ++--
 agent/findkey.c           |   65 ++-
 agent/pksign.c            |   41 +-
 common/ssh-utils.c        |  141 +++--
 tools/gpg-connect-agent.c |    5 +-
 8 files changed, 1044 insertions(+), 680 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list