pubkey: Move sexp parsing of remaining fucntions to the modules.
[libgcrypt.git] / cipher / ecc.c
index ae55f8c..bd4d253 100644 (file)
@@ -1,22 +1,22 @@
 /* ecc.c  -  Elliptic Curve Cryptography
  Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
-
-   This file is part of Libgcrypt.
-
-   Libgcrypt is free software; you can redistribute it and/or modify
-   it under the terms of the GNU Lesser General Public License as
-   published by the Free Software Foundation; either version 2.1 of
-   the License, or (at your option) any later version.
-
-   Libgcrypt is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  USA.  */
* Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
 
 /* This code is originally based on the Patch 0.1.6 for the gnupg
    1.4.x branch as retrieved on 2007-03-21 from
@@ -47,7 +47,6 @@
   - In mpi/ec.c we use mpi_powm for x^2 mod p: Either implement a
     special case in mpi_powm or check whether mpi_mulm is faster.
 
-  - Decide whether we should hide the mpi_point_t definition.
 */
 
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 
 #include "g10lib.h"
 #include "mpi.h"
 #include "cipher.h"
+#include "context.h"
+#include "ec-context.h"
+#include "pubkey-internal.h"
+#include "ecc-common.h"
 
-/* Definition of a curve.  */
-typedef struct
-{
-  gcry_mpi_t p;   /* Prime specifying the field GF(p).  */
-  gcry_mpi_t a;   /* First coefficient of the Weierstrass equation.  */
-  gcry_mpi_t b;   /* Second coefficient of the Weierstrass equation.  */
-  mpi_point_t G;  /* Base point (generator).  */
-  gcry_mpi_t n;   /* Order of G.  */
-} elliptic_curve_t;
-
-
-typedef struct
-{
-  elliptic_curve_t E;
-  mpi_point_t Q;  /* Q = [d]G  */
-} ECC_public_key;
-
-typedef struct
-{
-  elliptic_curve_t E;
-  mpi_point_t Q;
-  gcry_mpi_t d;
-} ECC_secret_key;
-
-
-/* This tables defines aliases for curve names.  */
-static const struct
-{
-  const char *name;  /* Our name.  */
-  const char *other; /* Other name. */
-} curve_aliases[] =
-  {
-    { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID  */
-    { "NIST P-192", "prime192v1" },          /* X9.62 name.  */
-    { "NIST P-192", "secp192r1"  },          /* SECP name.  */
-
-    { "NIST P-224", "secp224r1" },
-    { "NIST P-224", "1.3.132.0.33" },        /* SECP OID.  */
-
-    { "NIST P-256", "1.2.840.10045.3.1.7" }, /* From NIST SP 800-78-1.  */
-    { "NIST P-256", "prime256v1" },
-    { "NIST P-256", "secp256r1"  },
-
-    { "NIST P-384", "secp384r1" },
-    { "NIST P-384", "1.3.132.0.34" },
 
-    { "NIST P-521", "secp521r1" },
-    { "NIST P-521", "1.3.132.0.35" },
-
-    { "brainpoolP160r1", "1.3.36.3.3.2.8.1.1.1" },
-    { "brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3" },
-    { "brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5" },
-    { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7" },
-    { "brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9" },
-    { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11"},
-    { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13"},
-
-    { NULL, NULL}
-  };
-
-typedef struct   {
-  const char *desc;           /* Description of the curve.  */
-  unsigned int nbits;         /* Number of bits.  */
-  unsigned int fips:1;        /* True if this is a FIPS140-2 approved curve. */
-  const char  *p;             /* Order of the prime field.  */
-  const char *a, *b;          /* The coefficients. */
-  const char *n;              /* The order of the base point.  */
-  const char *g_x, *g_y;      /* Base point.  */
-} ecc_domain_parms_t;
-
-/* This static table defines all available curves.  */
-static const ecc_domain_parms_t domain_parms[] =
+static const char *ecc_names[] =
   {
-    {
-      "NIST P-192", 192, 1,
-      "0xfffffffffffffffffffffffffffffffeffffffffffffffff",
-      "0xfffffffffffffffffffffffffffffffefffffffffffffffc",
-      "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
-      "0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
-
-      "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
-      "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811"
-    },
-    {
-      "NIST P-224", 224, 1,
-      "0xffffffffffffffffffffffffffffffff000000000000000000000001",
-      "0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
-      "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
-      "0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d" ,
-
-      "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
-      "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
-    },
-    {
-      "NIST P-256", 256, 1,
-      "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
-      "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
-      "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
-      "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
-
-      "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
-      "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
-    },
-    {
-      "NIST P-384", 384, 1,
-      "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
-      "ffffffff0000000000000000ffffffff",
-      "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
-      "ffffffff0000000000000000fffffffc",
-      "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a"
-      "c656398d8a2ed19d2a85c8edd3ec2aef",
-      "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf"
-      "581a0db248b0a77aecec196accc52973",
-
-      "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38"
-      "5502f25dbf55296c3a545e3872760ab7",
-      "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0"
-      "0a60b1ce1d7e819d7a431d7c90ea0e5f"
-    },
-    {
-      "NIST P-521", 521, 1,
-      "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
-      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
-      "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
-      "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
-      "0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10"
-      "9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
-      "0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
-      "ffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
-
-      "0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d"
-      "baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
-      "0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6"
-      "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650"
-    },
-
-    { "brainpoolP160r1", 160, 0,
-      "0xe95e4a5f737059dc60dfc7ad95b3d8139515620f",
-      "0x340e7be2a280eb74e2be61bada745d97e8f7c300",
-      "0x1e589a8595423412134faa2dbdec95c8d8675e58",
-      "0xe95e4a5f737059dc60df5991d45029409e60fc09",
-      "0xbed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3",
-      "0x1667cb477a1a8ec338f94741669c976316da6321"
-    },
-
-    { "brainpoolP192r1", 192, 0,
-      "0xc302f41d932a36cda7a3463093d18db78fce476de1a86297",
-      "0x6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef",
-      "0x469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9",
-      "0xc302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
-      "0xc0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6",
-      "0x14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f"
-    },
-
-    { "brainpoolP224r1", 224, 0,
-      "0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
-      "0x68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43",
-      "0x2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b",
-      "0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
-      "0x0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d",
-      "0x58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd"
-    },
-
-    { "brainpoolP256r1", 256, 0,
-      "0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
-      "0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
-      "0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
-      "0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
-      "0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
-      "0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997"
-    },
-
-    { "brainpoolP320r1", 320, 0,
-      "0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28"
-      "fcd412b1f1b32e27",
-      "0x3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f4"
-      "92f375a97d860eb4",
-      "0x520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd88453981"
-      "6f5eb4ac8fb1f1a6",
-      "0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e9"
-      "8691555b44c59311",
-      "0x43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c7"
-      "10af8d0d39e20611",
-      "0x14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7"
-      "d35245d1692e8ee1"
-    },
-
-    { "brainpoolP384r1", 384, 0,
-      "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123"
-      "acd3a729901d1a71874700133107ec53",
-      "0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f"
-      "8aa5814a503ad4eb04a8c7dd22ce2826",
-      "0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d5"
-      "7cb4390295dbc9943ab78696fa504c11",
-      "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7"
-      "cf3ab6af6b7fc3103b883202e9046565",
-      "0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8"
-      "e826e03436d646aaef87b2e247d4af1e",
-      "0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff9912928"
-      "0e4646217791811142820341263c5315"
-    },
-
-    { "brainpoolP512r1", 512, 0,
-      "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330871"
-      "7d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
-      "0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc"
-      "2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
-      "0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a7"
-      "2bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
-      "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870"
-      "553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
-      "0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098e"
-      "ff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822",
-      "0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111"
-      "b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892"
-    },
-
-    { NULL, 0, 0, NULL, NULL, NULL, NULL }
+    "ecc",
+    "ecdsa",
+    "ecdh",
+    "eddsa",
+    NULL,
   };
 
 
@@ -286,22 +80,21 @@ static void (*progress_cb) (void *, const char*, int, int, int);
 static void *progress_cb_data;
 
 
-#define point_init(a)  _gcry_mpi_ec_point_init ((a))
-#define point_free(a)  _gcry_mpi_ec_point_free ((a))
-
+#define point_init(a)  _gcry_mpi_point_init ((a))
+#define point_free(a)  _gcry_mpi_point_free_parts ((a))
 
 \f
 /* Local prototypes. */
-static gcry_mpi_t gen_k (gcry_mpi_t p, int security_level);
 static void test_keys (ECC_secret_key * sk, unsigned int nbits);
 static int check_secret_key (ECC_secret_key * sk);
-static gpg_err_code_t sign (gcry_mpi_t input, ECC_secret_key *skey,
-                            gcry_mpi_t r, gcry_mpi_t s);
-static gpg_err_code_t verify (gcry_mpi_t input, ECC_public_key *pkey,
-                              gcry_mpi_t r, gcry_mpi_t s);
-
+static gpg_err_code_t sign_ecdsa (gcry_mpi_t input, ECC_secret_key *skey,
+                                  gcry_mpi_t r, gcry_mpi_t s,
+                                  int flags, int hashalgo);
+static gpg_err_code_t verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
+                                    gcry_mpi_t r, gcry_mpi_t s);
 
 static gcry_mpi_t gen_y_2 (gcry_mpi_t x, elliptic_curve_t * base);
+static unsigned int ecc_get_nbits (gcry_sexp_t parms);
 
 
 
@@ -325,91 +118,8 @@ _gcry_register_pk_ecc_progress (void (*cb) (void *, const char *,
 
 \f
 
-/* Set the value from S into D.  */
-static void
-point_set (mpi_point_t *d, mpi_point_t *s)
-{
-  mpi_set (d->x, s->x);
-  mpi_set (d->y, s->y);
-  mpi_set (d->z, s->z);
-}
-
-
-/*
- * Release a curve object.
- */
-static void
-curve_free (elliptic_curve_t *E)
-{
-  mpi_free (E->p); E->p = NULL;
-  mpi_free (E->a); E->a = NULL;
-  mpi_free (E->b);  E->b = NULL;
-  point_free (&E->G);
-  mpi_free (E->n);  E->n = NULL;
-}
-
-
-/*
- * Release a PK object.
- */
-static void
-ecc_pk_free (ECC_public_key *pk)
-{
-  point_free (&pk->Q);
-  curve_free (&pk->E);
-}
-
-
-/*
- * Release a SK object.
- */
-static void
-ecc_sk_free (ECC_secret_key *sk)
-{
-  point_free (&sk->Q);
-  curve_free (&sk->E);
-  mpi_free (sk->d);  sk->d = NULL;
-}
-
-
 /*
- * Return a copy of a curve object.
- */
-static elliptic_curve_t
-curve_copy (elliptic_curve_t E)
-{
-  elliptic_curve_t R;
-
-  R.p = mpi_copy (E.p);
-  R.a = mpi_copy (E.a);
-  R.b = mpi_copy (E.b);
-  point_init (&R.G);
-  point_set (&R.G, &E.G);
-  R.n = mpi_copy (E.n);
-
-  return R;
-}
-
-
-/* Helper to scan a hex string. */
-static gcry_mpi_t
-scanval (const char *string)
-{
-  gpg_error_t err;
-  gcry_mpi_t val;
-
-  err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
-  if (err)
-    log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (err));
-  return val;
-}
-
-
-
-\f
-
-/****************
- * Solve the right side of the equation that defines a curve.
+ * Solve the right side of the Weierstrass equation.
  */
 static gcry_mpi_t
 gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base)
@@ -433,158 +143,74 @@ gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base)
 }
 
 
-/* Generate a random secret scalar k with an order of p
-
-   At the beginning this was identical to the code is in elgamal.c.
-   Later imporved by mmr.   Further simplified by wk.  */
-static gcry_mpi_t
-gen_k (gcry_mpi_t p, int security_level)
-{
-  gcry_mpi_t k;
-  unsigned int nbits;
-
-  nbits = mpi_get_nbits (p);
-  k = mpi_snew (nbits);
-  if (DBG_CIPHER)
-    log_debug ("choosing a random k of %u bits\n", nbits);
-
-  gcry_mpi_randomize (k, nbits, security_level);
-
-  mpi_mod (k, k, p);  /*  k = k mod p  */
-
-  return k;
-}
-
-
-/* Generate the crypto system setup.  This function takes the NAME of
-   a curve or the desired number of bits and stores at R_CURVE the
-   parameters of the named curve or those of a suitable curve.  The
-   chosen number of bits is stored on R_NBITS.  */
+/* Standard version of the key generation.  */
 static gpg_err_code_t
-fill_in_curve (unsigned int nbits, const char *name,
-               elliptic_curve_t *curve, unsigned int *r_nbits)
+nist_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx,
+                   gcry_random_level_t random_level, unsigned int nbits)
 {
-  int idx, aliasno;
-
-  if (name)
-    {
-      /* First check nor native curves.  */
-      for (idx = 0; domain_parms[idx].desc; idx++)
-        if (!strcmp (name, domain_parms[idx].desc))
-          break;
-      /* If not found consult the alias table.  */
-      if (!domain_parms[idx].desc)
-        {
-          for (aliasno = 0; curve_aliases[aliasno].name; aliasno++)
-            if (!strcmp (name, curve_aliases[aliasno].other))
-              break;
-          if (curve_aliases[aliasno].name)
-            {
-              for (idx = 0; domain_parms[idx].desc; idx++)
-                if (!strcmp (curve_aliases[aliasno].name,
-                             domain_parms[idx].desc))
-                  break;
-            }
-        }
-    }
-  else
-    {
-      for (idx = 0; domain_parms[idx].desc; idx++)
-        if (nbits == domain_parms[idx].nbits)
-          break;
-    }
-  if (!domain_parms[idx].desc)
-    return GPG_ERR_INV_VALUE;
+  mpi_point_struct Q;
 
-  /* In fips mode we only support NIST curves.  Note that it is
-     possible to bypass this check by specifying the curve parameters
-     directly.  */
-  if (fips_mode () && !domain_parms[idx].fips )
-    return GPG_ERR_NOT_SUPPORTED;
+  point_init (&Q);
 
-  *r_nbits = domain_parms[idx].nbits;
-  curve->p = scanval (domain_parms[idx].p);
-  curve->a = scanval (domain_parms[idx].a);
-  curve->b = scanval (domain_parms[idx].b);
-  curve->n = scanval (domain_parms[idx].n);
-  curve->G.x = scanval (domain_parms[idx].g_x);
-  curve->G.y = scanval (domain_parms[idx].g_y);
-  curve->G.z = mpi_alloc_set_ui (1);
+  /* Generate a secret.  */
+  sk->d = _gcry_dsa_gen_k (E->n, random_level);
 
-  return 0;
-}
+  /* Compute Q.  */
+  _gcry_mpi_ec_mul_point (&Q, sk->d, &E->G, ctx);
 
+  /* Copy the stuff to the key structures. */
+  sk->E.model = E->model;
+  sk->E.dialect = E->dialect;
+  sk->E.p = mpi_copy (E->p);
+  sk->E.a = mpi_copy (E->a);
+  sk->E.b = mpi_copy (E->b);
+  point_init (&sk->E.G);
+  point_set (&sk->E.G, &E->G);
+  sk->E.n = mpi_copy (E->n);
+  point_init (&sk->Q);
 
-/*
- * First obtain the setup.  Over the finite field randomize an scalar
- * secret value, and calculate the public point.
- */
-static gpg_err_code_t
-generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name,
-              int transient_key,
-              gcry_mpi_t g_x, gcry_mpi_t g_y,
-              gcry_mpi_t q_x, gcry_mpi_t q_y)
-{
-  gpg_err_code_t err;
-  elliptic_curve_t E;
-  gcry_mpi_t d;
-  mpi_point_t Q;
-  mpi_ec_t ctx;
-  gcry_random_level_t random_level;
+  /* We want the Q=(x,y) be a "compliant key" in terms of the
+   * http://tools.ietf.org/html/draft-jivsov-ecc-compact, which simply
+   * means that we choose either Q=(x,y) or -Q=(x,p-y) such that we
+   * end up with the min(y,p-y) as the y coordinate.  Such a public
+   * key allows the most efficient compression: y can simply be
+   * dropped because we know that it's a minimum of the two
+   * possibilities without any loss of security.  */
+  {
+    gcry_mpi_t x, y, p_y;
+    const unsigned int pbits = mpi_get_nbits (E->p);
 
-  err = fill_in_curve (nbits, name, &E, &nbits);
-  if (err)
-    return err;
+    x = mpi_new (pbits);
+    y = mpi_new (pbits);
+    p_y = mpi_new (pbits);
 
-  if (DBG_CIPHER)
-    {
-      log_mpidump ("ecgen curve  p", E.p);
-      log_mpidump ("ecgen curve  a", E.a);
-      log_mpidump ("ecgen curve  b", E.b);
-      log_mpidump ("ecgen curve  n", E.n);
-      log_mpidump ("ecgen curve Gx", E.G.x);
-      log_mpidump ("ecgen curve Gy", E.G.y);
-      log_mpidump ("ecgen cyrve Gz", E.G.z);
-    }
+    if (_gcry_mpi_ec_get_affine (x, y, &Q, ctx))
+      log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
 
-  random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM;
-  if (DBG_CIPHER)
-    log_debug ("choosing a random x of size %u%s\n", nbits,
-               transient_key? " (transient-key)":"");
-  d = gen_k (E.n, random_level);
+    mpi_sub (p_y, E->p, y);    /* p_y = p - y */
 
-  /* Compute Q.  */
-  point_init (&Q);
-  ctx = _gcry_mpi_ec_init (E.p, E.a);
-  _gcry_mpi_ec_mul_point (&Q, d, &E.G, ctx);
+    if (mpi_cmp (p_y, y) < 0)   /* p - y < p */
+      {
+        /* We need to end up with -Q; this assures that new Q's y is
+           the smallest one */
+        mpi_sub (sk->d, E->n, sk->d);   /* d = order - d */
+        gcry_mpi_point_snatch_set (&sk->Q, x, p_y, mpi_alloc_set_ui (1));
 
-  /* Copy the stuff to the key structures. */
-  sk->E.p = mpi_copy (E.p);
-  sk->E.a = mpi_copy (E.a);
-  sk->E.b = mpi_copy (E.b);
-  point_init (&sk->E.G);
-  point_set (&sk->E.G, &E.G);
-  sk->E.n = mpi_copy (E.n);
-  point_init (&sk->Q);
-  point_set (&sk->Q, &Q);
-  sk->d    = mpi_copy (d);
-  /* We also return copies of G and Q in affine coordinates if
-     requested.  */
-  if (g_x && g_y)
-    {
-      if (_gcry_mpi_ec_get_affine (g_x, g_y, &sk->E.G, ctx))
-        log_fatal ("ecgen: Failed to get affine coordinates\n");
-    }
-  if (q_x && q_y)
-    {
-      if (_gcry_mpi_ec_get_affine (q_x, q_y, &sk->Q, ctx))
-        log_fatal ("ecgen: Failed to get affine coordinates\n");
-    }
-  _gcry_mpi_ec_free (ctx);
+      if (DBG_CIPHER)
+        log_debug ("ecgen converted Q to a compliant point\n");
+      }
+    else /* p - y >= p */
+      {
+        /* No change is needed exactly 50% of the time: just copy. */
+        point_set (&sk->Q, &Q);
+        if (DBG_CIPHER)
+          log_debug ("ecgen didn't need to convert Q to a compliant point\n");
 
-  point_free (&Q);
-  mpi_free (d);
-  curve_free (&E);
+        mpi_free (p_y);
+        mpi_free (x);
+      }
+    mpi_free (y);
+  }
 
   /* Now we can test our keys (this should never fail!).  */
   test_keys (sk, nbits - 64);
@@ -604,7 +230,7 @@ test_keys (ECC_secret_key *sk, unsigned int nbits)
 {
   ECC_public_key pk;
   gcry_mpi_t test = mpi_new (nbits);
-  mpi_point_t R_;
+  mpi_point_struct R_;
   gcry_mpi_t c = mpi_new (nbits);
   gcry_mpi_t out = mpi_new (nbits);
   gcry_mpi_t r = mpi_new (nbits);
@@ -615,16 +241,16 @@ test_keys (ECC_secret_key *sk, unsigned int nbits)
 
   point_init (&R_);
 
-  pk.E = curve_copy (sk->E);
+  pk.E = _gcry_ecc_curve_copy (sk->E);
   point_init (&pk.Q);
   point_set (&pk.Q, &sk->Q);
 
   gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
 
-  if (sign (test, sk, r, s) )
+  if (sign_ecdsa (test, sk, r, s, 0, 0) )
     log_fatal ("ECDSA operation: sign failed\n");
 
-  if (verify (test, &pk, r, s))
+  if (verify_ecdsa (test, &pk, r, s))
     {
       log_fatal ("ECDSA operation: sign, verify failed\n");
     }
@@ -632,7 +258,8 @@ test_keys (ECC_secret_key *sk, unsigned int nbits)
   if (DBG_CIPHER)
     log_debug ("ECDSA operation: sign, verify ok.\n");
 
-  ecc_pk_free (&pk);
+  point_free (&pk.Q);
+  _gcry_ecc_curve_free (&pk.E);
 
   point_free (&R_);
   mpi_free (s);
@@ -650,79 +277,141 @@ test_keys (ECC_secret_key *sk, unsigned int nbits)
 static int
 check_secret_key (ECC_secret_key * sk)
 {
-  mpi_point_t Q;
-  gcry_mpi_t y_2, y2 = mpi_alloc (0);
-  mpi_ec_t ctx;
+  int rc = 1;
+  mpi_point_struct Q;
+  gcry_mpi_t y_2, y2;
+  gcry_mpi_t x1, x2;
+  mpi_ec_t ctx = NULL;
+
+  point_init (&Q);
 
   /* ?primarity test of 'p' */
   /*  (...) //!! */
   /* G in E(F_p) */
   y_2 = gen_y_2 (sk->E.G.x, &sk->E);   /*  y^2=x^3+a*x+b */
+  y2 = mpi_alloc (0);
+  x1 = mpi_alloc (0);
+  x2 = mpi_alloc (0);
   mpi_mulm (y2, sk->E.G.y, sk->E.G.y, sk->E.p);      /*  y^2=y*y */
   if (mpi_cmp (y_2, y2))
     {
       if (DBG_CIPHER)
         log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n");
-      return (1);
+      goto leave;
     }
   /* G != PaI */
   if (!mpi_cmp_ui (sk->E.G.z, 0))
     {
       if (DBG_CIPHER)
         log_debug ("Bad check: 'G' cannot be Point at Infinity!\n");
-      return (1);
+      goto leave;
     }
 
-  point_init (&Q);
-  ctx = _gcry_mpi_ec_init (sk->E.p, sk->E.a);
+  ctx = _gcry_mpi_ec_p_internal_new (sk->E.model, sk->E.dialect,
+                                     sk->E.p, sk->E.a, sk->E.b);
+
   _gcry_mpi_ec_mul_point (&Q, sk->E.n, &sk->E.G, ctx);
   if (mpi_cmp_ui (Q.z, 0))
     {
       if (DBG_CIPHER)
         log_debug ("check_secret_key: E is not a curve of order n\n");
-      point_free (&Q);
-      _gcry_mpi_ec_free (ctx);
-      return 1;
+      goto leave;
     }
   /* pubkey cannot be PaI */
   if (!mpi_cmp_ui (sk->Q.z, 0))
     {
       if (DBG_CIPHER)
         log_debug ("Bad check: Q can not be a Point at Infinity!\n");
-      _gcry_mpi_ec_free (ctx);
-      return (1);
+      goto leave;
     }
   /* pubkey = [d]G over E */
   _gcry_mpi_ec_mul_point (&Q, sk->d, &sk->E.G, ctx);
-  if ((Q.x == sk->Q.x) && (Q.y == sk->Q.y) && (Q.z == sk->Q.z))
+
+  if (_gcry_mpi_ec_get_affine (x1, y_2, &Q, ctx))
     {
       if (DBG_CIPHER)
-        log_debug
-          ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
-      _gcry_mpi_ec_free (ctx);
-      return (1);
+        log_debug ("Bad check: Q can not be a Point at Infinity!\n");
+      goto leave;
+    }
+
+  /* Fast path for loaded secret keys - Q is already in affine coordinates */
+  if (!mpi_cmp_ui (sk->Q.z, 1))
+    {
+      if (mpi_cmp (x1, sk->Q.x) || mpi_cmp (y_2, sk->Q.y))
+        {
+          if (DBG_CIPHER)
+            log_debug
+              ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
+          goto leave;
+        }
+    }
+  else
+    {
+      if (_gcry_mpi_ec_get_affine (x2, y2, &sk->Q, ctx))
+        {
+          if (DBG_CIPHER)
+            log_debug ("Bad check: Q can not be a Point at Infinity!\n");
+          goto leave;
+        }
+
+      if (mpi_cmp (x1, x2) || mpi_cmp (y_2, y2))
+        {
+          if (DBG_CIPHER)
+            log_debug
+              ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
+          goto leave;
+        }
     }
+  rc = 0; /* Okay.  */
+
+ leave:
   _gcry_mpi_ec_free (ctx);
+  mpi_free (x2);
+  mpi_free (x1);
+  mpi_free (y2);
+  mpi_free (y_2);
   point_free (&Q);
-  return 0;
+  return rc;
 }
 
 
-/*
+/* Compute an ECDSA signature.
  * Return the signature struct (r,s) from the message hash.  The caller
  * must have allocated R and S.
  */
 static gpg_err_code_t
-sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
+sign_ecdsa (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s,
+            int flags, int hashalgo)
 {
   gpg_err_code_t err = 0;
+  int extraloops = 0;
   gcry_mpi_t k, dr, sum, k_1, x;
-  mpi_point_t I;
+  mpi_point_struct I;
+  gcry_mpi_t hash;
+  const void *abuf;
+  unsigned int abits, qbits;
   mpi_ec_t ctx;
 
   if (DBG_CIPHER)
     log_mpidump ("ecdsa sign hash  ", input );
 
+  qbits = mpi_get_nbits (skey->E.n);
+
+  /* Convert the INPUT into an MPI if needed.  */
+  if (mpi_is_opaque (input))
+    {
+      abuf = gcry_mpi_get_opaque (input, &abits);
+      err = gpg_err_code (gcry_mpi_scan (&hash, GCRYMPI_FMT_USG,
+                                         abuf, (abits+7)/8, NULL));
+      if (err)
+        return err;
+      if (abits > qbits)
+        gcry_mpi_rshift (hash, hash, abits - qbits);
+    }
+  else
+    hash = input;
+
+
   k = NULL;
   dr = mpi_alloc (0);
   sum = mpi_alloc (0);
@@ -730,21 +419,41 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
   x = mpi_alloc (0);
   point_init (&I);
 
-  mpi_set_ui (s, 0);
-  mpi_set_ui (r, 0);
-
-  ctx = _gcry_mpi_ec_init (skey->E.p, skey->E.a);
+  ctx = _gcry_mpi_ec_p_internal_new (skey->E.model, skey->E.dialect,
+                                     skey->E.p, skey->E.a, skey->E.b);
 
-  while (!mpi_cmp_ui (s, 0)) /* s == 0 */
+  /* Two loops to avoid R or S are zero.  This is more of a joke than
+     a real demand because the probability of them being zero is less
+     than any hardware failure.  Some specs however require it.  */
+  do
     {
-      while (!mpi_cmp_ui (r, 0)) /* r == 0 */
+      do
         {
-          /* Note, that we are guaranteed to enter this loop at least
-             once because r has been intialized to 0.  We can't use a
-             do_while because we want to keep the value of R even if S
-             has to be recomputed.  */
           mpi_free (k);
-          k = gen_k (skey->E.n, GCRY_STRONG_RANDOM);
+          k = NULL;
+          if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
+            {
+              /* Use Pornin's method for deterministic DSA.  If this
+                 flag is set, it is expected that HASH is an opaque
+                 MPI with the to be signed hash.  That hash is also
+                 used as h1 from 3.2.a.  */
+              if (!mpi_is_opaque (input))
+                {
+                  err = GPG_ERR_CONFLICT;
+                  goto leave;
+                }
+
+              abuf = gcry_mpi_get_opaque (input, &abits);
+              err = _gcry_dsa_gen_rfc6979_k (&k, skey->E.n, skey->d,
+                                             abuf, (abits+7)/8,
+                                             hashalgo, extraloops);
+              if (err)
+                goto leave;
+              extraloops++;
+            }
+          else
+            k = _gcry_dsa_gen_k (skey->E.n, GCRY_STRONG_RANDOM);
+
           _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx);
           if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx))
             {
@@ -755,11 +464,14 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
             }
           mpi_mod (r, x, skey->E.n);  /* r = x mod n */
         }
+      while (!mpi_cmp_ui (r, 0));
+
       mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n  */
-      mpi_addm (sum, input, dr, skey->E.n); /* sum = hash + (d*r) mod n  */
+      mpi_addm (sum, hash, dr, skey->E.n);  /* sum = hash + (d*r) mod n  */
       mpi_invm (k_1, k, skey->E.n);         /* k_1 = k^(-1) mod n  */
       mpi_mulm (s, k_1, sum, skey->E.n);    /* s = k^(-1)*(hash+(d*r)) mod n */
     }
+  while (!mpi_cmp_ui (s, 0));
 
   if (DBG_CIPHER)
     {
@@ -776,19 +488,23 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
   mpi_free (dr);
   mpi_free (k);
 
+  if (hash != input)
+    mpi_free (hash);
+
   return err;
 }
 
 
-/*
+/* Verify an ECDSA signature.
  * Check if R and S verifies INPUT.
  */
 static gpg_err_code_t
-verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
+verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
+              gcry_mpi_t r, gcry_mpi_t s)
 {
   gpg_err_code_t err = 0;
-  gcry_mpi_t h, h1, h2, x, y;
-  mpi_point_t Q, Q1, Q2;
+  gcry_mpi_t h, h1, h2, x;
+  mpi_point_struct Q, Q1, Q2;
   mpi_ec_t ctx;
 
   if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, pkey->E.n) < 0) )
@@ -800,37 +516,25 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
   h1 = mpi_alloc (0);
   h2 = mpi_alloc (0);
   x = mpi_alloc (0);
-  y = mpi_alloc (0);
   point_init (&Q);
   point_init (&Q1);
   point_init (&Q2);
 
-  ctx = _gcry_mpi_ec_init (pkey->E.p, pkey->E.a);
+  ctx = _gcry_mpi_ec_p_internal_new (pkey->E.model, pkey->E.dialect,
+                                     pkey->E.p, pkey->E.a, pkey->E.b);
 
   /* h  = s^(-1) (mod n) */
   mpi_invm (h, s, pkey->E.n);
-/*   log_mpidump ("   h", h); */
   /* h1 = hash * s^(-1) (mod n) */
   mpi_mulm (h1, input, h, pkey->E.n);
-/*   log_mpidump ("  h1", h1); */
   /* Q1 = [ hash * s^(-1) ]G  */
   _gcry_mpi_ec_mul_point (&Q1, h1, &pkey->E.G, ctx);
-/*   log_mpidump ("Q1.x", Q1.x); */
-/*   log_mpidump ("Q1.y", Q1.y); */
-/*   log_mpidump ("Q1.z", Q1.z); */
   /* h2 = r * s^(-1) (mod n) */
   mpi_mulm (h2, r, h, pkey->E.n);
-/*   log_mpidump ("  h2", h2); */
   /* Q2 = [ r * s^(-1) ]Q */
   _gcry_mpi_ec_mul_point (&Q2, h2, &pkey->Q, ctx);
-/*   log_mpidump ("Q2.x", Q2.x); */
-/*   log_mpidump ("Q2.y", Q2.y); */
-/*   log_mpidump ("Q2.z", Q2.z); */
   /* Q  = ([hash * s^(-1)]G) + ([r * s^(-1)]Q) */
   _gcry_mpi_ec_add_points (&Q, &Q1, &Q2, ctx);
-/*   log_mpidump (" Q.x", Q.x); */
-/*   log_mpidump (" Q.y", Q.y); */
-/*   log_mpidump (" Q.z", Q.z); */
 
   if (!mpi_cmp_ui (Q.z, 0))
     {
@@ -839,7 +543,7 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
       err = GPG_ERR_BAD_SIGNATURE;
       goto leave;
     }
-  if (_gcry_mpi_ec_get_affine (x, y, &Q, ctx))
+  if (_gcry_mpi_ec_get_affine (x, NULL, &Q, ctx))
     {
       if (DBG_CIPHER)
         log_debug ("ecc verify: Failed to get affine coordinates\n");
@@ -851,11 +555,10 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
     {
       if (DBG_CIPHER)
         {
-          log_mpidump ("   x", x);
-          log_mpidump ("   y", y);
-          log_mpidump ("   r", r);
-          log_mpidump ("   s", s);
-          log_debug ("ecc verify: Not verified (x != y)\n");
+          log_mpidump ("     x", x);
+          log_mpidump ("     r", r);
+          log_mpidump ("     s", s);
+          log_debug ("ecc verify: Not verified\n");
         }
       err = GPG_ERR_BAD_SIGNATURE;
       goto leave;
@@ -868,7 +571,6 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
   point_free (&Q2);
   point_free (&Q1);
   point_free (&Q);
-  mpi_free (y);
   mpi_free (x);
   mpi_free (h2);
   mpi_free (h1);
@@ -878,375 +580,1220 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
 
 
 \f
-/*********************************************
- **************  interface  ******************
- *********************************************/
-static gcry_mpi_t
-ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
+static void
+reverse_buffer (unsigned char *buffer, unsigned int length)
+{
+  unsigned int tmp, i;
+
+  for (i=0; i < length/2; i++)
+    {
+      tmp = buffer[i];
+      buffer[i] = buffer[length-1-i];
+      buffer[length-1-i] = tmp;
+    }
+}
+
+
+/* Encode MPI using the EdDSA scheme.  MINLEN specifies the required
+   length of the buffer in bytes.  On success 0 is returned an a
+   malloced buffer with the encoded point is stored at R_BUFFER; the
+   length of this buffer is stored at R_BUFLEN.  */
+static gpg_err_code_t
+eddsa_encodempi (gcry_mpi_t mpi, unsigned int minlen,
+                 unsigned char **r_buffer, unsigned int *r_buflen)
 {
-  gpg_error_t err;
-  int pbytes = (mpi_get_nbits (p)+7)/8;
-  size_t n;
-  unsigned char *buf, *ptr;
-  gcry_mpi_t result;
-
-  buf = gcry_xmalloc ( 1 + 2*pbytes );
-  *buf = 04; /* Uncompressed point.  */
-  ptr = buf+1;
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
-  if (err)
-    log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
-  if (n < pbytes)
-    {
-      memmove (ptr+(pbytes-n), ptr, n);
-      memset (ptr, 0, (pbytes-n));
-    }
-  ptr += pbytes;
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y);
-  if (err)
-    log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
-  if (n < pbytes)
-    {
-      memmove (ptr+(pbytes-n), ptr, n);
-      memset (ptr, 0, (pbytes-n));
-    }
-
-  err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL);
-  if (err)
-    log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
-  gcry_free (buf);
-
-  return result;
+  unsigned char *rawmpi;
+  unsigned int rawmpilen;
+
+  rawmpi = _gcry_mpi_get_buffer (mpi, minlen, &rawmpilen, NULL);
+  if (!rawmpi)
+    return gpg_err_code_from_syserror ();
+
+  *r_buffer = rawmpi;
+  *r_buflen = rawmpilen;
+  return 0;
 }
 
 
-/* RESULT must have been initialized and is set on success to the
-   point given by VALUE.  */
-static gcry_error_t
-os2ec (mpi_point_t *result, gcry_mpi_t value)
+/* Encode (X,Y) using the EdDSA scheme.  MINLEN is the required length
+   in bytes for the result.  On success 0 is returned and a malloced
+   buffer with the encoded point is stored at R_BUFFER; the length of
+   this buffer is stored at R_BUFLEN.  */
+static gpg_err_code_t
+eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int minlen,
+                  unsigned char **r_buffer, unsigned int *r_buflen)
+{
+  unsigned char *rawmpi;
+  unsigned int rawmpilen;
+
+  rawmpi = _gcry_mpi_get_buffer (y, minlen, &rawmpilen, NULL);
+  if (!rawmpi)
+    return gpg_err_code_from_syserror ();
+  if (mpi_test_bit (x, 0) && rawmpilen)
+    rawmpi[rawmpilen - 1] |= 0x80;  /* Set sign bit.  */
+
+  *r_buffer = rawmpi;
+  *r_buflen = rawmpilen;
+  return 0;
+}
+
+/* Encode POINT using the EdDSA scheme.  X and Y are either scratch
+   variables supplied by the caller or NULL.  CTX is the usual
+   context.  On success 0 is returned and a malloced buffer with the
+   encoded point is stored at R_BUFFER; the length of this buffer is
+   stored at R_BUFLEN.  */
+gpg_err_code_t
+_gcry_ecc_eddsa_encodepoint (mpi_point_t point, mpi_ec_t ec,
+                             gcry_mpi_t x_in, gcry_mpi_t y_in,
+                             unsigned char **r_buffer, unsigned int *r_buflen)
 {
-  gcry_error_t err;
-  size_t n;
-  unsigned char *buf;
+  gpg_err_code_t rc;
   gcry_mpi_t x, y;
 
-  n = (mpi_get_nbits (value)+7)/8;
-  buf = gcry_xmalloc (n);
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, n, &n, value);
-  if (err)
+  x = x_in? x_in : mpi_new (0);
+  y = y_in? y_in : mpi_new (0);
+
+  if (_gcry_mpi_ec_get_affine (x, y, point, ec))
     {
-      gcry_free (buf);
-      return err;
+      log_error ("eddsa_encodepoint: Failed to get affine coordinates\n");
+      rc = GPG_ERR_INTERNAL;
     }
-  if (n < 1)
+  else
+    rc = eddsa_encode_x_y (x, y, ec->nbits/8, r_buffer, r_buflen);
+
+  if (!x_in)
+    mpi_free (x);
+  if (!y_in)
+    mpi_free (y);
+  return rc;
+}
+
+
+/* Decode the EdDSA style encoded PK and set it into RESULT.  CTX is
+   the usual curve context.  If R_ENCPK is not NULL, the encoded PK is
+   stored at that address; this is a new copy to be released by the
+   caller.  In contrast to the supplied PK, this is not an MPI and
+   thus guarnateed to be properly padded.  R_ENCPKLEN received the
+   length of that encoded key.  */
+gpg_err_code_t
+_gcry_ecc_eddsa_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result,
+                             unsigned char **r_encpk, unsigned int *r_encpklen)
+{
+  gpg_err_code_t rc;
+  unsigned char *rawmpi;
+  unsigned int rawmpilen;
+  gcry_mpi_t yy, t, x, p1, p2, p3;
+  int sign;
+
+  if (mpi_is_opaque (pk))
     {
-      gcry_free (buf);
-      return GPG_ERR_INV_OBJ;
+      const unsigned char *buf;
+
+      buf = gcry_mpi_get_opaque (pk, &rawmpilen);
+      if (!buf)
+        return GPG_ERR_INV_OBJ;
+      rawmpilen = (rawmpilen + 7)/8;
+
+      /* First check whether the public key has been given in standard
+         uncompressed format.  No need to recover x in this case.
+         Detection is easy: The size of the buffer will be odd and the
+         first byte be 0x04.  */
+      if (rawmpilen > 1 && buf[0] == 0x04 && (rawmpilen%2))
+        {
+          gcry_mpi_t y;
+
+          rc = gcry_mpi_scan (&x, GCRYMPI_FMT_STD,
+                              buf+1, (rawmpilen-1)/2, NULL);
+          if (rc)
+            return rc;
+          rc = gcry_mpi_scan (&y, GCRYMPI_FMT_STD,
+                              buf+1+(rawmpilen-1)/2, (rawmpilen-1)/2, NULL);
+          if (rc)
+            {
+              mpi_free (x);
+              return rc;
+            }
+
+          if (r_encpk)
+            {
+              rc = eddsa_encode_x_y (x, y, ctx->nbits/8, r_encpk, r_encpklen);
+              if (rc)
+                {
+                  mpi_free (x);
+                  mpi_free (y);
+                  return rc;
+                }
+            }
+          mpi_snatch (result->x, x);
+          mpi_snatch (result->y, y);
+          mpi_set_ui (result->z, 1);
+          return 0;
+        }
+
+      /* EdDSA compressed point.  */
+      rawmpi = gcry_malloc (rawmpilen? rawmpilen:1);
+      if (!rawmpi)
+        return gpg_err_code_from_syserror ();
+      memcpy (rawmpi, buf, rawmpilen);
+      reverse_buffer (rawmpi, rawmpilen);
     }
-  if (*buf != 4)
+  else
     {
-      gcry_free (buf);
-      return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression.  */
+      /* Note: Without using an opaque MPI it is not reliable possible
+         to find out whether the public key has been given in
+         uncompressed format.  Thus we expect EdDSA format here.  */
+      rawmpi = _gcry_mpi_get_buffer (pk, ctx->nbits/8, &rawmpilen, NULL);
+      if (!rawmpi)
+        return gpg_err_code_from_syserror ();
     }
-  if ( ((n-1)%2) )
+
+  if (rawmpilen)
     {
-      gcry_free (buf);
-      return GPG_ERR_INV_OBJ;
+      sign = !!(rawmpi[0] & 0x80);
+      rawmpi[0] &= 0x7f;
     }
-  n = (n-1)/2;
-  err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
-  if (err)
+  else
+    sign = 0;
+  _gcry_mpi_set_buffer (result->y, rawmpi, rawmpilen, 0);
+  if (r_encpk)
     {
-      gcry_free (buf);
-      return err;
+      /* Revert to little endian.  */
+      if (sign && rawmpilen)
+        rawmpi[0] |= 0x80;
+      reverse_buffer (rawmpi, rawmpilen);
+      *r_encpk = rawmpi;
+      if (r_encpklen)
+        *r_encpklen = rawmpilen;
     }
-  err = gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
-  gcry_free (buf);
-  if (err)
+  else
+    gcry_free (rawmpi);
+
+  /* Now recover X.  */
+  /* t = (y^2-1) · ((b*y^2+1)^{p-2} mod p) */
+  x = mpi_new (0);
+  yy = mpi_new (0);
+  mpi_mul (yy, result->y, result->y);
+  t = mpi_copy (yy);
+  mpi_mul (t, t, ctx->b);
+  mpi_add_ui (t, t, 1);
+  p2 = mpi_copy (ctx->p);
+  mpi_sub_ui (p2, p2, 2);
+  mpi_powm (t, t, p2, ctx->p);
+
+  mpi_sub_ui (yy, yy, 1);
+  mpi_mul (t, yy, t);
+
+  /* x = t^{(p+3)/8} mod p */
+  p3 = mpi_copy (ctx->p);
+  mpi_add_ui (p3, p3, 3);
+  mpi_fdiv_q (p3, p3, mpi_const (MPI_C_EIGHT));
+  mpi_powm (x, t, p3, ctx->p);
+
+  /* (x^2 - t) % p != 0 ? x = (x*(2^{(p-1)/4} mod p)) % p */
+  mpi_mul (yy, x, x);
+  mpi_subm (yy, yy, t, ctx->p);
+  if (mpi_cmp_ui (yy, 0))
     {
-      mpi_free (x);
-      return err;
+      p1 = mpi_copy (ctx->p);
+      mpi_sub_ui (p1, p1, 1);
+      mpi_fdiv_q (p1, p1, mpi_const (MPI_C_FOUR));
+      mpi_powm (yy, mpi_const (MPI_C_TWO), p1, ctx->p);
+      mpi_mulm (x, x, yy, ctx->p);
     }
+  else
+    p1 = NULL;
+
+  /* is_odd(x) ? x = p-x */
+  if (mpi_test_bit (x, 0))
+    mpi_sub (x, ctx->p, x);
+
+  /* lowbit(x) != highbit(input) ?  x = p-x */
+  if (mpi_test_bit (x, 0) != sign)
+    mpi_sub (x, ctx->p, x);
 
   mpi_set (result->x, x);
-  mpi_set (result->y, y);
   mpi_set_ui (result->z, 1);
 
-  mpi_free (x);
-  mpi_free (y);
+  gcry_mpi_release (x);
+  gcry_mpi_release (yy);
+  gcry_mpi_release (t);
+  gcry_mpi_release (p3);
+  gcry_mpi_release (p2);
+  gcry_mpi_release (p1);
 
   return 0;
 }
 
 
-/* Extended version of ecc_generate.  */
-static gcry_err_code_t
-ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue,
-                  const gcry_sexp_t genparms,
-                  gcry_mpi_t *skey, gcry_mpi_t **retfactors,
-                  gcry_sexp_t *r_extrainfo)
+/* Ed25519 version of the key generation.  */
+static gpg_err_code_t
+eddsa_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx,
+                    gcry_random_level_t random_level)
 {
-  gpg_err_code_t ec;
-  ECC_secret_key sk;
-  gcry_mpi_t g_x, g_y, q_x, q_y;
-  char *curve_name = NULL;
-  gcry_sexp_t l1;
-  int transient_key = 0;
+  gpg_err_code_t rc;
+  int b = 256/8;             /* The only size we currently support.  */
+  gcry_mpi_t a, x, y;
+  mpi_point_struct Q;
+  char *dbuf;
+  size_t dlen;
+  gcry_buffer_t hvec[1];
+  unsigned char *hash_d = NULL;
 
-  (void)algo;
-  (void)evalue;
-  (void)r_extrainfo;
+  point_init (&Q);
+  memset (hvec, 0, sizeof hvec);
 
-  if (genparms)
-    {
-      /* Parse the optional "curve" parameter. */
-      l1 = gcry_sexp_find_token (genparms, "curve", 0);
-      if (l1)
-        {
-          curve_name = _gcry_sexp_nth_string (l1, 1);
-          gcry_sexp_release (l1);
-          if (!curve_name)
-            return GPG_ERR_INV_OBJ; /* No curve name or value too large. */
-        }
+  a = mpi_snew (0);
+  x = mpi_new (0);
+  y = mpi_new (0);
 
-      /* Parse the optional transient-key flag.  */
-      l1 = gcry_sexp_find_token (genparms, "transient-key", 0);
-      if (l1)
-        {
-          transient_key = 1;
-          gcry_sexp_release (l1);
-        }
+  /* Generate a secret.  */
+  hash_d = gcry_malloc_secure (2*b);
+  if (!hash_d)
+    {
+      rc = gpg_error_from_syserror ();
+      goto leave;
     }
+  dlen = b;
+  dbuf = gcry_random_bytes_secure (dlen, random_level);
+
+  /* Compute the A value.  */
+  hvec[0].data = dbuf;
+  hvec[0].len = dlen;
+  rc = _gcry_md_hash_buffers (GCRY_MD_SHA512, 0, hash_d, hvec, 1);
+  if (rc)
+    goto leave;
+  sk->d = _gcry_mpi_set_opaque (NULL, dbuf, dlen*8);
+  dbuf = NULL;
+  reverse_buffer (hash_d, 32);  /* Only the first half of the hash.  */
+  hash_d[0] = (hash_d[0] & 0x7f) | 0x40;
+  hash_d[31] &= 0xf8;
+  _gcry_mpi_set_buffer (a, hash_d, 32, 0);
+  gcry_free (hash_d); hash_d = NULL;
+  /* log_printmpi ("ecgen         a", a); */
 
-  /* NBITS is required if no curve name has been given.  */
-  if (!nbits && !curve_name)
-    return GPG_ERR_NO_OBJ; /* No NBITS parameter. */
+  /* Compute Q.  */
+  _gcry_mpi_ec_mul_point (&Q, a, &E->G, ctx);
+  if (DBG_CIPHER)
+    log_printpnt ("ecgen      pk", &Q, ctx);
 
-  g_x = mpi_new (0);
-  g_y = mpi_new (0);
-  q_x = mpi_new (0);
-  q_y = mpi_new (0);
-  ec = generate_key (&sk, nbits, curve_name, transient_key, g_x, g_y, q_x, q_y);
-  gcry_free (curve_name);
-  if (ec)
-    return ec;
-
-  skey[0] = sk.E.p;
-  skey[1] = sk.E.a;
-  skey[2] = sk.E.b;
-  skey[3] = ec2os (g_x, g_y, sk.E.p);
-  skey[4] = sk.E.n;
-  skey[5] = ec2os (q_x, q_y, sk.E.p);
-  skey[6] = sk.d;
-
-  mpi_free (g_x);
-  mpi_free (g_y);
-  mpi_free (q_x);
-  mpi_free (q_y);
+  /* Copy the stuff to the key structures. */
+  sk->E.model = E->model;
+  sk->E.dialect = E->dialect;
+  sk->E.p = mpi_copy (E->p);
+  sk->E.a = mpi_copy (E->a);
+  sk->E.b = mpi_copy (E->b);
+  point_init (&sk->E.G);
+  point_set (&sk->E.G, &E->G);
+  sk->E.n = mpi_copy (E->n);
+  point_init (&sk->Q);
+  point_set (&sk->Q, &Q);
 
-  point_free (&sk.E.G);
-  point_free (&sk.Q);
+ leave:
+  gcry_mpi_release (a);
+  gcry_mpi_release (x);
+  gcry_mpi_release (y);
+  gcry_free (hash_d);
+  return rc;
+}
 
-  /* Make an empty list of factors.  */
-  *retfactors = gcry_calloc ( 1, sizeof **retfactors );
-  if (!*retfactors)
-    return gpg_err_code_from_syserror ();
 
-  if (DBG_CIPHER)
+/* Compute an EdDSA signature. See:
+ *   [ed25519] 23pp. (PDF) Daniel J. Bernstein, Niels Duif, Tanja
+ *   Lange, Peter Schwabe, Bo-Yin Yang. High-speed high-security
+ *   signatures.  Journal of Cryptographic Engineering 2 (2012), 77-89.
+ *   Document ID: a1a62a2f76d23f65d622484ddd09caf8.
+ *   URL: http://cr.yp.to/papers.html#ed25519. Date: 2011.09.26.
+ *
+ * Despite that this function requires the specification of a hash
+ * algorithm, we only support what has been specified by the paper.
+ * This may change in the future.  Note that we don't check the used
+ * curve; the user is responsible to use Ed25519.
+ *
+ * Return the signature struct (r,s) from the message hash.  The caller
+ * must have allocated R_R and S.
+ */
+static gpg_err_code_t
+sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
+            gcry_mpi_t r_r, gcry_mpi_t s, int hashalgo, gcry_mpi_t pk)
+{
+  int rc;
+  mpi_ec_t ctx = NULL;
+  int b;
+  unsigned int tmp;
+  unsigned char *digest;
+  gcry_buffer_t hvec[3];
+  const void *mbuf;
+  size_t mlen;
+  unsigned char *rawmpi = NULL;
+  unsigned int rawmpilen;
+  unsigned char *encpk = NULL; /* Encoded public key.  */
+  unsigned int encpklen;
+  mpi_point_struct I;          /* Intermediate value.  */
+  mpi_point_struct Q;          /* Public key.  */
+  gcry_mpi_t a, x, y, r;
+
+  memset (hvec, 0, sizeof hvec);
+
+  if (!mpi_is_opaque (input))
+    return GPG_ERR_INV_DATA;
+  if (hashalgo != GCRY_MD_SHA512)
+    return GPG_ERR_DIGEST_ALGO;
+
+  /* Initialize some helpers.  */
+  point_init (&I);
+  point_init (&Q);
+  a = mpi_snew (0);
+  x = mpi_new (0);
+  y = mpi_new (0);
+  r = mpi_new (0);
+  ctx = _gcry_mpi_ec_p_internal_new (skey->E.model, skey->E.dialect,
+                                     skey->E.p, skey->E.a, skey->E.b);
+  b = (ctx->nbits+7)/8;
+  if (b != 256/8)
+    return GPG_ERR_INTERNAL; /* We only support 256 bit. */
+
+  digest = gcry_calloc_secure (2, b);
+  if (!digest)
     {
-      log_mpidump ("ecgen result p", skey[0]);
-      log_mpidump ("ecgen result a", skey[1]);
-      log_mpidump ("ecgen result b", skey[2]);
-      log_mpidump ("ecgen result G", skey[3]);
-      log_mpidump ("ecgen result n", skey[4]);
-      log_mpidump ("ecgen result Q", skey[5]);
-      log_mpidump ("ecgen result d", skey[6]);
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
     }
 
-  return 0;
+  /* Hash the secret key.  We clear DIGEST so we can use it as input
+     to left pad the key with zeroes for hashing.  */
+  rawmpi = _gcry_mpi_get_buffer (skey->d, 0, &rawmpilen, NULL);
+  if (!rawmpi)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+  hvec[0].data = digest;
+  hvec[0].off = 0;
+  hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
+  hvec[1].data = rawmpi;
+  hvec[1].off = 0;
+  hvec[1].len = rawmpilen;
+  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+  gcry_free (rawmpi); rawmpi = NULL;
+  if (rc)
+    goto leave;
+
+  /* Compute the A value (this modifies DIGEST).  */
+  reverse_buffer (digest, 32);  /* Only the first half of the hash.  */
+  digest[0] = (digest[0] & 0x7f) | 0x40;
+  digest[31] &= 0xf8;
+  _gcry_mpi_set_buffer (a, digest, 32, 0);
+
+  /* Compute the public key if it has not been supplied as optional
+     parameter.  */
+  if (pk)
+    {
+      rc = _gcry_ecc_eddsa_decodepoint (pk, ctx, &Q,  &encpk, &encpklen);
+      if (rc)
+        goto leave;
+      if (DBG_CIPHER)
+        log_printhex ("* e_pk", encpk, encpklen);
+      if (!_gcry_mpi_ec_curve_point (&Q, ctx))
+        {
+          rc = GPG_ERR_BROKEN_PUBKEY;
+          goto leave;
+        }
+    }
+  else
+    {
+      _gcry_mpi_ec_mul_point (&Q, a, &skey->E.G, ctx);
+      rc = _gcry_ecc_eddsa_encodepoint (&Q, ctx, x, y, &encpk, &encpklen);
+      if (rc)
+        goto leave;
+      if (DBG_CIPHER)
+        log_printhex ("  e_pk", encpk, encpklen);
+    }
+
+  /* Compute R.  */
+  mbuf = gcry_mpi_get_opaque (input, &tmp);
+  mlen = (tmp +7)/8;
+  if (DBG_CIPHER)
+    log_printhex ("     m", mbuf, mlen);
+
+  hvec[0].data = digest;
+  hvec[0].off  = 32;
+  hvec[0].len  = 32;
+  hvec[1].data = (char*)mbuf;
+  hvec[1].len  = mlen;
+  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+  if (rc)
+    goto leave;
+  reverse_buffer (digest, 64);
+  if (DBG_CIPHER)
+    log_printhex ("     r", digest, 64);
+  _gcry_mpi_set_buffer (r, digest, 64, 0);
+  _gcry_mpi_ec_mul_point (&I, r, &skey->E.G, ctx);
+  if (DBG_CIPHER)
+    log_printpnt ("   r", &I, ctx);
+
+  /* Convert R into affine coordinates and apply encoding.  */
+  rc = _gcry_ecc_eddsa_encodepoint (&I, ctx, x, y, &rawmpi, &rawmpilen);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_printhex ("   e_r", rawmpi, rawmpilen);
+
+  /* S = r + a * H(encodepoint(R) + encodepoint(pk) + m) mod n  */
+  hvec[0].data = rawmpi;  /* (this is R) */
+  hvec[0].off  = 0;
+  hvec[0].len  = rawmpilen;
+  hvec[1].data = encpk;
+  hvec[1].off  = 0;
+  hvec[1].len  = encpklen;
+  hvec[2].data = (char*)mbuf;
+  hvec[2].off  = 0;
+  hvec[2].len  = mlen;
+  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+  if (rc)
+    goto leave;
+
+  /* No more need for RAWMPI thus we now transfer it to R_R.  */
+  gcry_mpi_set_opaque (r_r, rawmpi, rawmpilen*8);
+  rawmpi = NULL;
+
+  reverse_buffer (digest, 64);
+  if (DBG_CIPHER)
+    log_printhex (" H(R+)", digest, 64);
+  _gcry_mpi_set_buffer (s, digest, 64, 0);
+  mpi_mulm (s, s, a, skey->E.n);
+  mpi_addm (s, s, r, skey->E.n);
+  rc = eddsa_encodempi (s, b, &rawmpi, &rawmpilen);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_printhex ("   e_s", rawmpi, rawmpilen);
+  gcry_mpi_set_opaque (s, rawmpi, rawmpilen*8);
+  rawmpi = NULL;
+
+  rc = 0;
+
+ leave:
+  gcry_mpi_release (a);
+  gcry_mpi_release (x);
+  gcry_mpi_release (y);
+  gcry_mpi_release (r);
+  gcry_free (digest);
+  _gcry_mpi_ec_free (ctx);
+  point_free (&I);
+  point_free (&Q);
+  gcry_free (encpk);
+  gcry_free (rawmpi);
+  return rc;
 }
 
 
-static gcry_err_code_t
-ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
-              gcry_mpi_t *skey, gcry_mpi_t **retfactors)
+/* Verify an EdDSA signature.  See sign_eddsa for the reference.
+ * Check if R_IN and S_IN verifies INPUT.  PKEY has the curve
+ * parameters and PK is the EdDSA style encoded public key.
+ */
+static gpg_err_code_t
+verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
+              gcry_mpi_t r_in, gcry_mpi_t s_in, int hashalgo, gcry_mpi_t pk)
 {
-  (void)evalue;
-  return ecc_generate_ext (algo, nbits, 0, NULL, skey, retfactors, NULL);
+  int rc;
+  mpi_ec_t ctx = NULL;
+  int b;
+  unsigned int tmp;
+  mpi_point_struct Q;          /* Public key.  */
+  unsigned char *encpk = NULL; /* Encoded public key.  */
+  unsigned int encpklen;
+  const void *mbuf, *rbuf;
+  unsigned char *tbuf = NULL;
+  size_t mlen, rlen;
+  unsigned int tlen;
+  unsigned char digest[64];
+  gcry_buffer_t hvec[3];
+  gcry_mpi_t h, s;
+  mpi_point_struct Ia, Ib;
+
+  if (!mpi_is_opaque (input) || !mpi_is_opaque (r_in) || !mpi_is_opaque (s_in))
+    return GPG_ERR_INV_DATA;
+  if (hashalgo != GCRY_MD_SHA512)
+    return GPG_ERR_DIGEST_ALGO;
+
+  point_init (&Q);
+  point_init (&Ia);
+  point_init (&Ib);
+  h = mpi_new (0);
+  s = mpi_new (0);
+
+  ctx = _gcry_mpi_ec_p_internal_new (pkey->E.model, pkey->E.dialect,
+                                     pkey->E.p, pkey->E.a, pkey->E.b);
+  b = ctx->nbits/8;
+  if (b != 256/8)
+    return GPG_ERR_INTERNAL; /* We only support 256 bit. */
+
+  /* Decode and check the public key.  */
+  rc = _gcry_ecc_eddsa_decodepoint (pk, ctx, &Q, &encpk, &encpklen);
+  if (rc)
+    goto leave;
+  if (!_gcry_mpi_ec_curve_point (&Q, ctx))
+    {
+      rc = GPG_ERR_BROKEN_PUBKEY;
+      goto leave;
+    }
+  if (DBG_CIPHER)
+    log_printhex ("  e_pk", encpk, encpklen);
+  if (encpklen != b)
+    {
+      rc = GPG_ERR_INV_LENGTH;
+      goto leave;
+    }
+
+  /* Convert the other input parameters.  */
+  mbuf = gcry_mpi_get_opaque (input, &tmp);
+  mlen = (tmp +7)/8;
+  if (DBG_CIPHER)
+    log_printhex ("     m", mbuf, mlen);
+  rbuf = gcry_mpi_get_opaque (r_in, &tmp);
+  rlen = (tmp +7)/8;
+  if (DBG_CIPHER)
+    log_printhex ("     r", rbuf, rlen);
+  if (rlen != b)
+    {
+      rc = GPG_ERR_INV_LENGTH;
+      goto leave;
+    }
+
+  /* h = H(encodepoint(R) + encodepoint(pk) + m)  */
+  hvec[0].data = (char*)rbuf;
+  hvec[0].off  = 0;
+  hvec[0].len  = rlen;
+  hvec[1].data = encpk;
+  hvec[1].off  = 0;
+  hvec[1].len  = encpklen;
+  hvec[2].data = (char*)mbuf;
+  hvec[2].off  = 0;
+  hvec[2].len  = mlen;
+  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+  if (rc)
+    goto leave;
+  reverse_buffer (digest, 64);
+  if (DBG_CIPHER)
+    log_printhex (" H(R+)", digest, 64);
+  _gcry_mpi_set_buffer (h, digest, 64, 0);
+
+  /* According to the paper the best way for verification is:
+         encodepoint(sG - h·Q) = encodepoint(r)
+     because we don't need to decode R. */
+  {
+    void *sbuf;
+    unsigned int slen;
+
+    sbuf = _gcry_mpi_get_opaque_copy (s_in, &tmp);
+    slen = (tmp +7)/8;
+    reverse_buffer (sbuf, slen);
+    if (DBG_CIPHER)
+      log_printhex ("     s", sbuf, slen);
+    _gcry_mpi_set_buffer (s, sbuf, slen, 0);
+    gcry_free (sbuf);
+    if (slen != b)
+      {
+        rc = GPG_ERR_INV_LENGTH;
+        goto leave;
+      }
+  }
+
+  _gcry_mpi_ec_mul_point (&Ia, s, &pkey->E.G, ctx);
+  _gcry_mpi_ec_mul_point (&Ib, h, &Q, ctx);
+  _gcry_mpi_neg (Ib.x, Ib.x);
+  _gcry_mpi_ec_add_points (&Ia, &Ia, &Ib, ctx);
+  rc = _gcry_ecc_eddsa_encodepoint (&Ia, ctx, s, h, &tbuf, &tlen);
+  if (rc)
+    goto leave;
+  if (tlen != rlen || memcmp (tbuf, rbuf, tlen))
+    {
+      if (DBG_CIPHER)
+        log_debug ("eddsa verify: Not verified\n");
+      rc = GPG_ERR_BAD_SIGNATURE;
+      goto leave;
+    }
+
+  if (DBG_CIPHER)
+    log_debug ("eddsa verify: Accepted\n");
+  rc = 0;
+
+ leave:
+  gcry_free (encpk);
+  gcry_free (tbuf);
+  _gcry_mpi_ec_free (ctx);
+  gcry_mpi_release (s);
+  gcry_mpi_release (h);
+  point_free (&Ia);
+  point_free (&Ib);
+  point_free (&Q);
+  return rc;
 }
 
 
-/* Return the parameters of the curve NAME.  */
+\f
+/*********************************************
+ **************  interface  ******************
+ *********************************************/
+
 static gcry_err_code_t
-ecc_get_param (const char *name, gcry_mpi_t *pkey)
+ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
 {
-  gpg_err_code_t err;
+  gpg_err_code_t rc;
   unsigned int nbits;
   elliptic_curve_t E;
-  mpi_ec_t ctx;
-  gcry_mpi_t g_x, g_y;
+  ECC_secret_key sk;
+  gcry_mpi_t x = NULL;
+  gcry_mpi_t y = NULL;
+  char *curve_name = NULL;
+  gcry_sexp_t l1;
+  int transient_key = 0;
+  gcry_random_level_t random_level;
+  mpi_ec_t ctx = NULL;
+  gcry_sexp_t curve_info = NULL;
+  gcry_mpi_t base = NULL;
+  gcry_mpi_t public = NULL;
+  gcry_mpi_t secret = NULL;
 
-  err = fill_in_curve (0, name, &E, &nbits);
-  if (err)
-    return err;
+  memset (&E, 0, sizeof E);
+  memset (&sk, 0, sizeof sk);
 
-  g_x = mpi_new (0);
-  g_y = mpi_new (0);
-  ctx = _gcry_mpi_ec_init (E.p, E.a);
-  if (_gcry_mpi_ec_get_affine (g_x, g_y, &E.G, ctx))
-    log_fatal ("ecc get param: Failed to get affine coordinates\n");
-  _gcry_mpi_ec_free (ctx);
-  point_free (&E.G);
+  rc = _gcry_pk_util_get_nbits (genparms, &nbits);
+  if (rc)
+    return rc;
 
-  pkey[0] = E.p;
-  pkey[1] = E.a;
-  pkey[2] = E.b;
-  pkey[3] = ec2os (g_x, g_y, E.p);
-  pkey[4] = E.n;
-  pkey[5] = NULL;
+  /* Parse the optional "curve" parameter. */
+  l1 = gcry_sexp_find_token (genparms, "curve", 0);
+  if (l1)
+    {
+      curve_name = _gcry_sexp_nth_string (l1, 1);
+      gcry_sexp_release (l1);
+      if (!curve_name)
+        return GPG_ERR_INV_OBJ; /* No curve name or value too large. */
+    }
 
-  mpi_free (g_x);
-  mpi_free (g_y);
+  /* Parse the optional transient-key flag.  */
+  l1 = gcry_sexp_find_token (genparms, "transient-key", 0);
+  if (l1)
+    {
+      transient_key = 1;
+      gcry_sexp_release (l1);
+    }
 
-  return 0;
+  /* NBITS is required if no curve name has been given.  */
+  if (!nbits && !curve_name)
+    return GPG_ERR_NO_OBJ; /* No NBITS parameter. */
+
+  rc = _gcry_ecc_fill_in_curve (nbits, curve_name, &E, &nbits);
+  gcry_free (curve_name); curve_name = NULL;
+  if (rc)
+    goto leave;
+
+  if (DBG_CIPHER)
+    {
+      log_debug ("ecgen curve info: %s/%s\n",
+                 _gcry_ecc_model2str (E.model),
+                 _gcry_ecc_dialect2str (E.dialect));
+      if (E.name)
+        log_debug ("ecgen curve used: %s\n", E.name);
+      log_printmpi ("ecgen curve   p", E.p);
+      log_printmpi ("ecgen curve   a", E.a);
+      log_printmpi ("ecgen curve   b", E.b);
+      log_printmpi ("ecgen curve   n", E.n);
+      log_printpnt ("ecgen curve G", &E.G, NULL);
+    }
+
+  random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM;
+  ctx = _gcry_mpi_ec_p_internal_new (E.model, E.dialect, E.p, E.a, E.b);
+  x = mpi_new (0);
+  y = mpi_new (0);
+
+  switch (E.dialect)
+    {
+    case ECC_DIALECT_STANDARD:
+      rc = nist_generate_key (&sk, &E, ctx, random_level, nbits);
+      break;
+    case ECC_DIALECT_ED25519:
+      rc = eddsa_generate_key (&sk, &E, ctx, random_level);
+      break;
+    default:
+      rc = GPG_ERR_INTERNAL;
+      break;
+    }
+  if (rc)
+    goto leave;
+
+  /* Copy data to the result.  */
+  if (_gcry_mpi_ec_get_affine (x, y, &sk.E.G, ctx))
+    log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "G");
+  base = _gcry_ecc_ec2os (x, y, sk.E.p);
+  if (sk.E.dialect == ECC_DIALECT_ED25519)
+    {
+      unsigned char *encpk;
+      unsigned int encpklen;
+
+      rc = _gcry_ecc_eddsa_encodepoint (&sk.Q, ctx, x, y, &encpk, &encpklen);
+      if (rc)
+        return rc;
+      public = mpi_new (0);
+      gcry_mpi_set_opaque (public, encpk, encpklen*8);
+      encpk = NULL;
+    }
+  else
+    {
+      if (_gcry_mpi_ec_get_affine (x, y, &sk.Q, ctx))
+        log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
+      public = _gcry_ecc_ec2os (x, y, sk.E.p);
+    }
+  secret = sk.d; sk.d = NULL;
+  if (E.name)
+    {
+      rc = gcry_sexp_build (&curve_info, NULL, "(curve %s)", E.name);
+      if (rc)
+        goto leave;
+    }
+
+  rc = gcry_sexp_build (r_skey, NULL,
+                        "(key-data"
+                        " (public-key"
+                        "  (ecc%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))"
+                        " (private-key"
+                        "  (ecc%S(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)(d%m)))"
+                        " )",
+                        curve_info,
+                        sk.E.p, sk.E.a, sk.E.b, base, sk.E.n, public,
+                        curve_info,
+                        sk.E.p, sk.E.a, sk.E.b, base, sk.E.n, public, secret);
+  if (rc)
+    goto leave;
+
+  if (DBG_CIPHER)
+    {
+      log_printmpi ("ecgen result  p", sk.E.p);
+      log_printmpi ("ecgen result  a", sk.E.a);
+      log_printmpi ("ecgen result  b", sk.E.b);
+      log_printmpi ("ecgen result  G", base);
+      log_printmpi ("ecgen result  n", sk.E.n);
+      log_printmpi ("ecgen result  Q", public);
+      log_printmpi ("ecgen result  d", secret);
+    }
+
+ leave:
+  mpi_free (secret);
+  mpi_free (public);
+  mpi_free (base);
+  {
+    _gcry_ecc_curve_free (&sk.E);
+    point_free (&sk.Q);
+    mpi_free (sk.d);
+  }
+  _gcry_ecc_curve_free (&E);
+  mpi_free (x);
+  mpi_free (y);
+  _gcry_mpi_ec_free (ctx);
+  gcry_sexp_release (curve_info);
+  return rc;
 }
 
 
 static gcry_err_code_t
-ecc_check_secret_key (int algo, gcry_mpi_t *skey)
+ecc_check_secret_key (gcry_sexp_t keyparms)
 {
-  gpg_err_code_t err;
+  gcry_err_code_t rc;
+  gcry_sexp_t l1 = NULL;
+  char *curvename = NULL;
+  gcry_mpi_t mpi_g = NULL;
+  gcry_mpi_t mpi_q = NULL;
   ECC_secret_key sk;
 
-  (void)algo;
-
-  /* FIXME:  This check looks a bit fishy:  Now long is the array?  */
-  if (!skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] || !skey[5]
-      || !skey[6])
-    return GPG_ERR_BAD_MPI;
-
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  err = os2ec (&sk.E.G, skey[3]);
-  if (err)
+  memset (&sk, 0, sizeof sk);
+
+  /*
+   * Extract the key.
+   */
+  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?/q?+d",
+                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                   &mpi_q, &sk.d, NULL);
+  if (rc)
+    goto leave;
+  if (mpi_g)
     {
-      point_free (&sk.E.G);
-      return err;
+      point_init (&sk.E.G);
+      rc = _gcry_ecc_os2ec (&sk.E.G, mpi_g);
+      if (rc)
+        goto leave;
     }
-  sk.E.n = skey[4];
-  point_init (&sk.Q);
-  err = os2ec (&sk.Q, skey[5]);
-  if (err)
+  /* Add missing parameters using the optional curve parameter.  */
+  gcry_sexp_release (l1);
+  l1 = gcry_sexp_find_token (keyparms, "curve", 5);
+  if (l1)
     {
-      point_free (&sk.E.G);
-      point_free (&sk.Q);
-      return err;
+      curvename = gcry_sexp_nth_string (l1, 1);
+      if (curvename)
+        {
+          rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
+          if (rc)
+            return rc;
+        }
+    }
+  /* Guess required fields if a curve parameter has not been given.
+     FIXME: This is a crude hacks.  We need to fix that.  */
+  if (!curvename)
+    {
+      sk.E.model = MPI_EC_WEIERSTRASS;
+      sk.E.dialect = ECC_DIALECT_STANDARD;
+    }
+  if (DBG_CIPHER)
+    {
+      log_debug ("ecc_testkey inf: %s/%s\n",
+                 _gcry_ecc_model2str (sk.E.model),
+                 _gcry_ecc_dialect2str (sk.E.dialect));
+      if (sk.E.name)
+        log_debug  ("ecc_testkey nam: %s\n", sk.E.name);
+      log_printmpi ("ecc_testkey   p", sk.E.p);
+      log_printmpi ("ecc_testkey   a", sk.E.a);
+      log_printmpi ("ecc_testkey   b", sk.E.b);
+      log_printpnt ("ecc_testkey g",   &sk.E.G, NULL);
+      log_printmpi ("ecc_testkey   n", sk.E.n);
+      log_printmpi ("ecc_testkey   q", mpi_q);
+      if (!fips_mode ())
+        log_printmpi ("ecc_testkey   d", sk.d);
+    }
+  if (!sk.E.p || !sk.E.a || !sk.E.b || !sk.E.G.x || !sk.E.n || !sk.d)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
     }
 
-  sk.d = skey[6];
-
-  if (check_secret_key (&sk))
+  if (mpi_q)
     {
-      point_free (&sk.E.G);
-      point_free (&sk.Q);
-      return GPG_ERR_BAD_SECKEY;
+      point_init (&sk.Q);
+      rc = _gcry_ecc_os2ec (&sk.Q, mpi_q);
+      if (rc)
+        goto leave;
     }
+  else
+    {
+      /* The current test requires Q.  */
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  if (check_secret_key (&sk))
+    rc = GPG_ERR_BAD_SECKEY;
+
+ leave:
+  gcry_mpi_release (sk.E.p);
+  gcry_mpi_release (sk.E.a);
+  gcry_mpi_release (sk.E.b);
+  gcry_mpi_release (mpi_g);
   point_free (&sk.E.G);
+  gcry_mpi_release (sk.E.n);
+  gcry_mpi_release (mpi_q);
   point_free (&sk.Q);
-  return 0;
+  gcry_mpi_release (sk.d);
+  gcry_free (curvename);
+  gcry_sexp_release (l1);
+  if (DBG_CIPHER)
+    log_debug ("ecc_testkey   => %s\n", gpg_strerror (rc));
+  return rc;
 }
 
 
 static gcry_err_code_t
-ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey)
+ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
 {
-  gpg_err_code_t err;
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_mpi_t data = NULL;
+  gcry_sexp_t l1 = NULL;
+  char *curvename = NULL;
+  gcry_mpi_t mpi_g = NULL;
+  gcry_mpi_t mpi_q = NULL;
   ECC_secret_key sk;
+  gcry_mpi_t sig_r = NULL;
+  gcry_mpi_t sig_s = NULL;
 
-  (void)algo;
+  memset (&sk, 0, sizeof sk);
 
-  if (!data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
-      || !skey[5] || !skey[6] )
-    return GPG_ERR_BAD_MPI;
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, 0);
 
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  err = os2ec (&sk.E.G, skey[3]);
-  if (err)
+  /* Extract the data.  */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_mpidump ("ecc_sign   data", data);
+
+  /*
+   * Extract the key.
+   */
+  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?/q?+d",
+                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                   &mpi_q, &sk.d, NULL);
+  if (rc)
+    goto leave;
+  if (mpi_g)
+    {
+      point_init (&sk.E.G);
+      rc = _gcry_ecc_os2ec (&sk.E.G, mpi_g);
+      if (rc)
+        goto leave;
+    }
+  /* Add missing parameters using the optional curve parameter.  */
+  gcry_sexp_release (l1);
+  l1 = gcry_sexp_find_token (keyparms, "curve", 5);
+  if (l1)
     {
-      point_free (&sk.E.G);
-      return err;
+      curvename = gcry_sexp_nth_string (l1, 1);
+      if (curvename)
+        {
+          rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
+          if (rc)
+            return rc;
+        }
     }
-  sk.E.n = skey[4];
-  point_init (&sk.Q);
-  err = os2ec (&sk.Q, skey[5]);
-  if (err)
+  /* Guess required fields if a curve parameter has not been given.
+     FIXME: This is a crude hacks.  We need to fix that.  */
+  if (!curvename)
+    {
+      sk.E.model = ((ctx.flags & PUBKEY_FLAG_EDDSA)
+                    ? MPI_EC_TWISTEDEDWARDS
+                    : MPI_EC_WEIERSTRASS);
+      sk.E.dialect = ((ctx.flags & PUBKEY_FLAG_EDDSA)
+                      ? ECC_DIALECT_ED25519
+                      : ECC_DIALECT_STANDARD);
+    }
+  if (DBG_CIPHER)
     {
-      point_free (&sk.E.G);
-      point_free (&sk.Q);
-      return err;
+      log_debug ("ecc_sign   info: %s/%s\n",
+                 _gcry_ecc_model2str (sk.E.model),
+                 _gcry_ecc_dialect2str (sk.E.dialect));
+      if (sk.E.name)
+        log_debug  ("ecc_sign   name: %s\n", sk.E.name);
+      log_printmpi ("ecc_sign      p", sk.E.p);
+      log_printmpi ("ecc_sign      a", sk.E.a);
+      log_printmpi ("ecc_sign      b", sk.E.b);
+      log_printpnt ("ecc_sign    g",   &sk.E.G, NULL);
+      log_printmpi ("ecc_sign      n", sk.E.n);
+      log_printmpi ("ecc_sign      q", mpi_q);
+      if (!fips_mode ())
+        log_printmpi ("ecc_sign      d", sk.d);
+    }
+  if (!sk.E.p || !sk.E.a || !sk.E.b || !sk.E.G.x || !sk.E.n || !sk.d)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
     }
-  sk.d = skey[6];
 
-  resarr[0] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
-  resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
-  err = sign (data, &sk, resarr[0], resarr[1]);
-  if (err)
+
+  sig_r = gcry_mpi_new (0);
+  sig_s = gcry_mpi_new (0);
+  if ((ctx.flags & PUBKEY_FLAG_EDDSA))
     {
-      mpi_free (resarr[0]);
-      mpi_free (resarr[1]);
-      resarr[0] = NULL; /* Mark array as released.  */
+      /* EdDSA requires the public key.  */
+      rc = sign_eddsa (data, &sk, sig_r, sig_s, ctx.hash_algo, mpi_q);
+      if (!rc)
+        rc = gcry_sexp_build (r_sig, NULL,
+                              "(sig-val(eddsa(r%M)(s%M)))", sig_r, sig_s);
     }
+  else
+    {
+      rc = sign_ecdsa (data, &sk, sig_r, sig_s, ctx.flags, ctx.hash_algo);
+      if (!rc)
+        rc = gcry_sexp_build (r_sig, NULL,
+                              "(sig-val(ecdsa(r%M)(s%M)))", sig_r, sig_s);
+    }
+
+
+ leave:
+  gcry_mpi_release (sk.E.p);
+  gcry_mpi_release (sk.E.a);
+  gcry_mpi_release (sk.E.b);
+  gcry_mpi_release (mpi_g);
   point_free (&sk.E.G);
+  gcry_mpi_release (sk.E.n);
+  gcry_mpi_release (mpi_q);
   point_free (&sk.Q);
-  return err;
+  gcry_mpi_release (sk.d);
+  gcry_mpi_release (sig_r);
+  gcry_mpi_release (sig_s);
+  gcry_free (curvename);
+  gcry_mpi_release (data);
+  gcry_sexp_release (l1);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("ecc_sign      => %s\n", gpg_strerror (rc));
+  return rc;
 }
 
 
 static gcry_err_code_t
-ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
-            int (*cmp)(void *, gcry_mpi_t), void *opaquev)
+ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
 {
-  gpg_err_code_t err;
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_sexp_t l1 = NULL;
+  char *curvename = NULL;
+  gcry_mpi_t mpi_g = NULL;
+  gcry_mpi_t mpi_q = NULL;
+  gcry_mpi_t sig_r = NULL;
+  gcry_mpi_t sig_s = NULL;
+  gcry_mpi_t data = NULL;
   ECC_public_key pk;
+  int sigflags;
 
-  (void)algo;
-  (void)cmp;
-  (void)opaquev;
+  memset (&pk, 0, sizeof pk);
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY,
+                                   ecc_get_nbits (s_keyparms));
+
+  /* Extract the data.  */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_mpidump ("ecc_verify data", data);
+
+  /*
+   * Extract the signature value.
+   */
+  rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags);
+  if (rc)
+    goto leave;
+  rc = _gcry_pk_util_extract_mpis (l1,
+                                   (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
+                                   &sig_r, &sig_s, NULL);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    {
+      log_mpidump ("ecc_verify  s_r", sig_r);
+      log_mpidump ("ecc_verify  s_s", sig_s);
+    }
+  if ((ctx.flags & PUBKEY_FLAG_EDDSA) ^ (sigflags & PUBKEY_FLAG_EDDSA))
+    {
+      rc = GPG_ERR_CONFLICT; /* Inconsistent use of flag/algoname.  */
+      goto leave;
+    }
 
-  if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] || !pkey[2]
-      || !pkey[3] || !pkey[4] || !pkey[5] )
-    return GPG_ERR_BAD_MPI;
 
-  pk.E.p = pkey[0];
-  pk.E.a = pkey[1];
-  pk.E.b = pkey[2];
-  point_init (&pk.E.G);
-  err = os2ec (&pk.E.G, pkey[3]);
-  if (err)
+  /*
+   * Extract the key.
+   */
+  rc = _gcry_pk_util_extract_mpis (s_keyparms, "-p?a?b?g?n?/q?",
+                                   &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
+                                   &mpi_q, NULL);
+  if (rc)
+    goto leave;
+  if (mpi_g)
     {
-      point_free (&pk.E.G);
-      return err;
+      point_init (&pk.E.G);
+      rc = _gcry_ecc_os2ec (&pk.E.G, mpi_g);
+      if (rc)
+        goto leave;
     }
-  pk.E.n = pkey[4];
-  point_init (&pk.Q);
-  err = os2ec (&pk.Q, pkey[5]);
-  if (err)
+  /* Add missing parameters using the optional curve parameter.  */
+  gcry_sexp_release (l1);
+  l1 = gcry_sexp_find_token (s_keyparms, "curve", 5);
+  if (l1)
+    {
+      curvename = gcry_sexp_nth_string (l1, 1);
+      if (curvename)
+        {
+          rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
+          if (rc)
+            return rc;
+        }
+    }
+  /* Guess required fields if a curve parameter has not been given.
+     FIXME: This is a crude hacks.  We need to fix that.  */
+  if (!curvename)
+    {
+      pk.E.model = ((sigflags & PUBKEY_FLAG_EDDSA)
+                    ? MPI_EC_TWISTEDEDWARDS
+                    : MPI_EC_WEIERSTRASS);
+      pk.E.dialect = ((sigflags & PUBKEY_FLAG_EDDSA)
+                      ? ECC_DIALECT_ED25519
+                      : ECC_DIALECT_STANDARD);
+    }
+
+  if (DBG_CIPHER)
     {
-      point_free (&pk.E.G);
-      point_free (&pk.Q);
-      return err;
+      log_debug ("ecc_verify info: %s/%s\n",
+                 _gcry_ecc_model2str (pk.E.model),
+                 _gcry_ecc_dialect2str (pk.E.dialect));
+      if (pk.E.name)
+        log_debug  ("ecc_verify name: %s\n", pk.E.name);
+      log_printmpi ("ecc_verify    p", pk.E.p);
+      log_printmpi ("ecc_verify    a", pk.E.a);
+      log_printmpi ("ecc_verify    b", pk.E.b);
+      log_printpnt ("ecc_verify  g",   &pk.E.G, NULL);
+      log_printmpi ("ecc_verify    n", pk.E.n);
+      log_printmpi ("ecc_verify    q", mpi_q);
+    }
+  if (!pk.E.p || !pk.E.a || !pk.E.b || !pk.E.G.x || !pk.E.n || !mpi_q)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
     }
 
-  err = verify (hash, &pk, data[0], data[1]);
 
+  /*
+   * Verify the signature.
+   */
+  if ((sigflags & PUBKEY_FLAG_EDDSA))
+    {
+      rc = verify_eddsa (data, &pk, sig_r, sig_s, ctx.hash_algo, mpi_q);
+    }
+  else
+    {
+      point_init (&pk.Q);
+      rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
+      if (rc)
+        goto leave;
+
+      if (mpi_is_opaque (data))
+        {
+          const void *abuf;
+          unsigned int abits, qbits;
+          gcry_mpi_t a;
+
+          qbits = mpi_get_nbits (pk.E.n);
+
+          abuf = gcry_mpi_get_opaque (data, &abits);
+          rc = gpg_err_code (gcry_mpi_scan (&a, GCRYMPI_FMT_USG,
+                                            abuf, (abits+7)/8, NULL));
+          if (!rc)
+            {
+              if (abits > qbits)
+                gcry_mpi_rshift (a, a, abits - qbits);
+
+              rc = verify_ecdsa (a, &pk, sig_r, sig_s);
+              gcry_mpi_release (a);
+            }
+        }
+      else
+        rc = verify_ecdsa (data, &pk, sig_r, sig_s);
+    }
+
+ leave:
+  gcry_mpi_release (pk.E.p);
+  gcry_mpi_release (pk.E.a);
+  gcry_mpi_release (pk.E.b);
+  gcry_mpi_release (mpi_g);
   point_free (&pk.E.G);
+  gcry_mpi_release (pk.E.n);
+  gcry_mpi_release (mpi_q);
   point_free (&pk.Q);
-  return err;
+  gcry_mpi_release (data);
+  gcry_mpi_release (sig_r);
+  gcry_mpi_release (sig_s);
+  gcry_free (curvename);
+  gcry_sexp_release (l1);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("ecc_verify    => %s\n", rc?gpg_strerror (rc):"Good");
+  return rc;
 }
 
 
@@ -1268,9 +1815,9 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
  * ecc_encrypt_raw description:
  *   input:
  *     data[0] : private scalar (k)
- *   output:
- *     result[0] : shared point (kdG)
- *     result[1] : generated ephemeral public key (kG)
+ *   output: A new S-expression with the parameters:
+ *     s : shared point (kdG)
+ *     e : generated ephemeral public key (kG)
  *
  * ecc_decrypt_raw description:
  *   input:
@@ -1279,46 +1826,110 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
  *     result[0] : shared point (kdG)
  */
 static gcry_err_code_t
-ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k,
-                 gcry_mpi_t *pkey, int flags)
+ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
 {
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_sexp_t l1 = NULL;
+  char *curvename = NULL;
+  gcry_mpi_t mpi_g = NULL;
+  gcry_mpi_t mpi_q = NULL;
+  gcry_mpi_t mpi_s = NULL;
+  gcry_mpi_t mpi_e = NULL;
+  gcry_mpi_t data = NULL;
   ECC_public_key pk;
-  mpi_ec_t ctx;
-  gcry_mpi_t result[2];
-  int err;
+  mpi_ec_t ec = NULL;
+
+  memset (&pk, 0, sizeof pk);
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
+                                   ecc_get_nbits (keyparms));
+
+  /*
+   * Extract the data.
+   */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_mpidump ("ecc_encrypt data", data);
+  if (mpi_is_opaque (data))
+    {
+      rc = GPG_ERR_INV_DATA;
+      goto leave;
+    }
 
-  (void)algo;
-  (void)flags;
 
-  if (!k
-      || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4] || !pkey[5])
-    return GPG_ERR_BAD_MPI;
+  /*
+   * Extract the key.
+   */
+  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?+q",
+                                   &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
+                                   &mpi_q, NULL);
+  if (rc)
+    goto leave;
+  if (mpi_g)
+    {
+      point_init (&pk.E.G);
+      rc = _gcry_ecc_os2ec (&pk.E.G, mpi_g);
+      if (rc)
+        goto leave;
+    }
+  /* Add missing parameters using the optional curve parameter.  */
+  gcry_sexp_release (l1);
+  l1 = gcry_sexp_find_token (keyparms, "curve", 5);
+  if (l1)
+    {
+      curvename = gcry_sexp_nth_string (l1, 1);
+      if (curvename)
+        {
+          rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
+          if (rc)
+            return rc;
+        }
+    }
+  /* Guess required fields if a curve parameter has not been given.  */
+  if (!curvename)
+    {
+      pk.E.model = MPI_EC_WEIERSTRASS;
+      pk.E.dialect = ECC_DIALECT_STANDARD;
+    }
 
-  pk.E.p = pkey[0];
-  pk.E.a = pkey[1];
-  pk.E.b = pkey[2];
-  point_init (&pk.E.G);
-  err = os2ec (&pk.E.G, pkey[3]);
-  if (err)
+  if (DBG_CIPHER)
     {
-      point_free (&pk.E.G);
-      return err;
+      log_debug ("ecc_encrypt info: %s/%s\n",
+                 _gcry_ecc_model2str (pk.E.model),
+                 _gcry_ecc_dialect2str (pk.E.dialect));
+      if (pk.E.name)
+        log_debug  ("ecc_encrypt name: %s\n", pk.E.name);
+      log_printmpi ("ecc_encrypt    p", pk.E.p);
+      log_printmpi ("ecc_encrypt    a", pk.E.a);
+      log_printmpi ("ecc_encrypt    b", pk.E.b);
+      log_printpnt ("ecc_encrypt  g",   &pk.E.G, NULL);
+      log_printmpi ("ecc_encrypt    n", pk.E.n);
+      log_printmpi ("ecc_encrypt    q", mpi_q);
     }
-  pk.E.n = pkey[4];
-  point_init (&pk.Q);
-  err = os2ec (&pk.Q, pkey[5]);
-  if (err)
+  if (!pk.E.p || !pk.E.a || !pk.E.b || !pk.E.G.x || !pk.E.n || !mpi_q)
     {
-      point_free (&pk.E.G);
-      point_free (&pk.Q);
-      return err;
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  /* Convert the public key.  */
+  if (mpi_q)
+    {
+      point_init (&pk.Q);
+      rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
+      if (rc)
+        goto leave;
     }
 
-  ctx = _gcry_mpi_ec_init (pk.E.p, pk.E.a);
+  /* Compute the encrypted value.  */
+  ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect,
+                                    pk.E.p, pk.E.a, pk.E.b);
 
   /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */
   {
-    mpi_point_t R;     /* Result that we return.  */
+    mpi_point_struct R;  /* Result that we return.  */
     gcry_mpi_t x, y;
 
     x = mpi_new (0);
@@ -1327,20 +1938,18 @@ ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k,
     point_init (&R);
 
     /* R = kQ  <=>  R = kdG  */
-    _gcry_mpi_ec_mul_point (&R, k, &pk.Q, ctx);
+    _gcry_mpi_ec_mul_point (&R, data, &pk.Q, ec);
 
-    if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+    if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
       log_fatal ("ecdh: Failed to get affine coordinates for kdG\n");
-
-    result[0] = ec2os (x, y, pk.E.p);
+    mpi_s = _gcry_ecc_ec2os (x, y, pk.E.p);
 
     /* R = kG */
-    _gcry_mpi_ec_mul_point (&R, k, &pk.E.G, ctx);
+    _gcry_mpi_ec_mul_point (&R, data, &pk.E.G, ec);
 
-    if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+    if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
       log_fatal ("ecdh: Failed to get affine coordinates for kG\n");
-
-    result[1] = ec2os (x, y, pk.E.p);
+    mpi_e = _gcry_ecc_ec2os (x, y, pk.E.p);
 
     mpi_free (x);
     mpi_free (y);
@@ -1348,23 +1957,30 @@ ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k,
     point_free (&R);
   }
 
-  _gcry_mpi_ec_free (ctx);
-  ecc_pk_free (&pk);
-
-  if (!result[0] || !result[1])
-    {
-      mpi_free (result[0]);
-      mpi_free (result[1]);
-      return GPG_ERR_ENOMEM;
-    }
+  rc = gcry_sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))",
+                        mpi_s, mpi_e);
 
-  /* Success.  */
-  resarr[0] = result[0];
-  resarr[1] = result[1];
-
-  return 0;
+ leave:
+  gcry_mpi_release (pk.E.p);
+  gcry_mpi_release (pk.E.a);
+  gcry_mpi_release (pk.E.b);
+  gcry_mpi_release (mpi_g);
+  point_free (&pk.E.G);
+  gcry_mpi_release (pk.E.n);
+  gcry_mpi_release (mpi_q);
+  point_free (&pk.Q);
+  gcry_mpi_release (data);
+  gcry_mpi_release (mpi_s);
+  gcry_mpi_release (mpi_e);
+  gcry_free (curvename);
+  _gcry_mpi_ec_free (ec);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("ecc_encrypt    => %s\n", gpg_strerror (rc));
+  return rc;
 }
 
+
 /*  input:
  *     data[0] : a point kG (ephemeral public key)
  *   output:
@@ -1373,65 +1989,115 @@ ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k,
  *  see ecc_encrypt_raw for details.
  */
 static gcry_err_code_t
-ecc_decrypt_raw (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
-                 gcry_mpi_t *skey, int flags)
+ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
 {
+  gpg_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_sexp_t l1 = NULL;
+  gcry_mpi_t data_e = NULL;
   ECC_secret_key sk;
-  mpi_point_t R;       /* Result that we return.  */
-  mpi_point_t kG;
-  mpi_ec_t ctx;
-  gcry_mpi_t r;
-  int err;
-
-  (void)algo;
-  (void)flags;
-
-  *result = NULL;
-
-  if (!data || !data[0]
-      || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
-      || !skey[5] || !skey[6] )
-    return GPG_ERR_BAD_MPI;
-
+  gcry_mpi_t mpi_g = NULL;
+  char *curvename = NULL;
+  mpi_ec_t ec = NULL;
+  mpi_point_struct kG;
+  mpi_point_struct R;
+  gcry_mpi_t r = NULL;
+
+  memset (&sk, 0, sizeof sk);
   point_init (&kG);
-  err = os2ec (&kG, data[0]);
-  if (err)
+  point_init (&R);
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
+                                   ecc_get_nbits (keyparms));
+
+  /*
+   * Extract the data.
+   */
+  rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
+  if (rc)
+    goto leave;
+  rc = _gcry_pk_util_extract_mpis (l1, "e", &data_e, NULL);
+  if (rc)
+    goto leave;
+  if (DBG_CIPHER)
+    log_printmpi ("ecc_decrypt  d_e", data_e);
+  if (mpi_is_opaque (data_e))
     {
-      point_free (&kG);
-      return err;
+      rc = GPG_ERR_INV_DATA;
+      goto leave;
     }
 
-
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  err = os2ec (&sk.E.G, skey[3]);
-  if (err)
+  /*
+   * Extract the key.
+   */
+  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?+d",
+                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                   &sk.d, NULL);
+  if (rc)
+    goto leave;
+  if (mpi_g)
     {
-      point_free (&kG);
-      point_free (&sk.E.G);
-      return err;
+      point_init (&sk.E.G);
+      rc = _gcry_ecc_os2ec (&sk.E.G, mpi_g);
+      if (rc)
+        goto leave;
+    }
+  /* Add missing parameters using the optional curve parameter.  */
+  gcry_sexp_release (l1);
+  l1 = gcry_sexp_find_token (keyparms, "curve", 5);
+  if (l1)
+    {
+      curvename = gcry_sexp_nth_string (l1, 1);
+      if (curvename)
+        {
+          rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
+          if (rc)
+            return rc;
+        }
     }
-  sk.E.n = skey[4];
-  point_init (&sk.Q);
-  err = os2ec (&sk.Q, skey[5]);
-  if (err)
+  /* Guess required fields if a curve parameter has not been given.  */
+  if (!curvename)
+    {
+      sk.E.model = MPI_EC_WEIERSTRASS;
+      sk.E.dialect = ECC_DIALECT_STANDARD;
+    }
+  if (DBG_CIPHER)
+    {
+      log_debug ("ecc_decrypt info: %s/%s\n",
+                 _gcry_ecc_model2str (sk.E.model),
+                 _gcry_ecc_dialect2str (sk.E.dialect));
+      if (sk.E.name)
+        log_debug  ("ecc_decrypt name: %s\n", sk.E.name);
+      log_printmpi ("ecc_decrypt    p", sk.E.p);
+      log_printmpi ("ecc_decrypt    a", sk.E.a);
+      log_printmpi ("ecc_decrypt    b", sk.E.b);
+      log_printpnt ("ecc_decrypt  g",   &sk.E.G, NULL);
+      log_printmpi ("ecc_decrypt    n", sk.E.n);
+      if (!fips_mode ())
+        log_printmpi ("ecc_decrypt    d", sk.d);
+    }
+  if (!sk.E.p || !sk.E.a || !sk.E.b || !sk.E.G.x || !sk.E.n || !sk.d)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+
+  /*
+   * Compute the plaintext.
+   */
+  rc = _gcry_ecc_os2ec (&kG, data_e);
+  if (rc)
     {
       point_free (&kG);
-      point_free (&sk.E.G);
-      point_free (&sk.Q);
-      return err;
+      return rc;
     }
-  sk.d = skey[6];
 
-  ctx = _gcry_mpi_ec_init (sk.E.p, sk.E.a);
+  ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect,
+                                    sk.E.p, sk.E.a, sk.E.b);
 
   /* R = dkG */
-  point_init (&R);
-  _gcry_mpi_ec_mul_point (&R, sk.d, &kG, ctx);
-
-  point_free (&kG);
+  _gcry_mpi_ec_mul_point (&R, sk.d, &kG, ec);
 
   /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so:  */
   {
@@ -1440,35 +2106,95 @@ ecc_decrypt_raw (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
     x = mpi_new (0);
     y = mpi_new (0);
 
-    if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+    if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
       log_fatal ("ecdh: Failed to get affine coordinates\n");
 
-    r = ec2os (x, y, sk.E.p);
+    r = _gcry_ecc_ec2os (x, y, sk.E.p);
+    if (!r)
+      rc = gpg_err_code_from_syserror ();
+    else
+      rc = 0;
     mpi_free (x);
     mpi_free (y);
   }
+  if (DBG_CIPHER)
+    log_printmpi ("ecc_decrypt  res", r);
 
-  point_free (&R);
-  _gcry_mpi_ec_free (ctx);
-  ecc_sk_free (&sk) ;
-
-  if (!r)
-    return GPG_ERR_ENOMEM;
-
-  /* Success.  */
-
-  *result = r;
+  if (!rc)
+    rc = gcry_sexp_build (r_plain, NULL, "(value %m)", r);
 
-  return 0;
+ leave:
+  point_free (&R);
+  point_free (&kG);
+  gcry_mpi_release (r);
+  gcry_mpi_release (sk.E.p);
+  gcry_mpi_release (sk.E.a);
+  gcry_mpi_release (sk.E.b);
+  gcry_mpi_release (mpi_g);
+  point_free (&sk.E.G);
+  gcry_mpi_release (sk.E.n);
+  gcry_mpi_release (sk.d);
+  gcry_mpi_release (data_e);
+  gcry_free (curvename);
+  gcry_sexp_release (l1);
+  _gcry_mpi_ec_free (ec);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("ecc_decrypt    => %s\n", gpg_strerror (rc));
+  return rc;
 }
 
 
+/* Return the number of bits for the key described by PARMS.  On error
+ * 0 is returned.  The format of PARMS starts with the algorithm name;
+ * for example:
+ *
+ *   (ecc
+ *     (p <mpi>)
+ *     (a <mpi>)
+ *     (b <mpi>)
+ *     (g <mpi>)
+ *     (n <mpi>)
+ *     (q <mpi>))
+ *
+ * More parameters may be given currently P is needed.  FIXME: We
+ * need allow for a "curve" parameter.
+ */
 static unsigned int
-ecc_get_nbits (int algo, gcry_mpi_t *pkey)
+ecc_get_nbits (gcry_sexp_t parms)
 {
-  (void)algo;
+  gcry_sexp_t l1;
+  gcry_mpi_t p;
+  unsigned int nbits = 0;
+  char *curve;
+
+  l1 = gcry_sexp_find_token (parms, "p", 1);
+  if (!l1)
+    { /* Parameter P not found - check whether we have "curve".  */
+      l1 = gcry_sexp_find_token (parms, "curve", 5);
+      if (!l1)
+        return 0; /* Neither P nor CURVE found.  */
 
-  return mpi_get_nbits (pkey[0]);
+      curve = _gcry_sexp_nth_string (l1, 1);
+      gcry_sexp_release (l1);
+      if (!curve)
+        return 0;  /* No curve name given (or out of core). */
+
+      if (_gcry_ecc_fill_in_curve (0, curve, NULL, &nbits))
+        nbits = 0;
+      gcry_free (curve);
+    }
+  else
+    {
+      p = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+      gcry_sexp_release (l1);
+      if (p)
+        {
+          nbits = mpi_get_nbits (p);
+          gcry_mpi_release (p);
+        }
+    }
+  return nbits;
 }
 
 
@@ -1515,12 +2241,13 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
         tmpvalues[idx] = NULL;
 
       curve = _gcry_sexp_nth_string (l1, 1);
+      gcry_sexp_release (l1);
       if (!curve)
         {
           ec = GPG_ERR_INV_OBJ; /* Name missing or out of core. */
           goto leave;
         }
-      ec = ecc_get_param (curve, tmpvalues);
+      ec = _gcry_ecc_get_param (curve, tmpvalues);
       gcry_free (curve);
       if (ec)
         goto leave;
@@ -1553,7 +2280,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
       unsigned char *rawmpi;
       unsigned int rawmpilen;
 
-      rawmpi = _gcry_mpi_get_buffer (values[idx], &rawmpilen, NULL);
+      rawmpi = _gcry_mpi_get_buffer (values[idx], 0, &rawmpilen, NULL);
       if (!rawmpi)
         {
           ec = gpg_err_code_from_syserror ();
@@ -1575,6 +2302,93 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
 }
 
 
+\f
+/*
+   Low-level API helper functions.
+ */
+
+/* This is the worker function for gcry_pubkey_get_sexp for ECC
+   algorithms.  Note that the caller has already stored NULL at
+   R_SEXP.  */
+gpg_err_code_t
+_gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
+{
+  gpg_err_code_t rc;
+  gcry_mpi_t mpi_G = NULL;
+  gcry_mpi_t mpi_Q = NULL;
+
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n)
+    return GPG_ERR_BAD_CRYPT_CTX;
+
+  if (mode == GCRY_PK_GET_SECKEY && !ec->d)
+    return GPG_ERR_NO_SECKEY;
+
+  /* Compute the public point if it is missing.  */
+  if (!ec->Q && ec->d)
+    ec->Q = _gcry_ecc_compute_public (NULL, ec);
+
+  /* Encode G and Q.  */
+  mpi_G = _gcry_mpi_ec_ec2os (ec->G, ec);
+  if (!mpi_G)
+    {
+      rc = GPG_ERR_BROKEN_PUBKEY;
+      goto leave;
+    }
+  if (!ec->Q)
+    {
+      rc = GPG_ERR_BAD_CRYPT_CTX;
+      goto leave;
+    }
+
+  if (ec->dialect == ECC_DIALECT_ED25519)
+    {
+      unsigned char *encpk;
+      unsigned int encpklen;
+
+      rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL,
+                                        &encpk, &encpklen);
+      if (rc)
+        goto leave;
+      mpi_Q = gcry_mpi_set_opaque (NULL, encpk, encpklen*8);
+      encpk = NULL;
+    }
+  else
+    {
+      mpi_Q = _gcry_mpi_ec_ec2os (ec->Q, ec);
+    }
+  if (!mpi_Q)
+    {
+      rc = GPG_ERR_BROKEN_PUBKEY;
+      goto leave;
+    }
+
+  /* Fixme: We should return a curve name instead of the parameters if
+     if know that they match a curve.  */
+
+  if (ec->d && (!mode || mode == GCRY_PK_GET_SECKEY))
+    {
+      /* Let's return a private key. */
+      rc = gcry_sexp_build
+        (r_sexp, NULL,
+         "(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)(d%m)))",
+         ec->p, ec->a, ec->b, mpi_G, ec->n, mpi_Q, ec->d);
+    }
+  else if (ec->Q)
+    {
+      /* Let's return a public key.  */
+      rc = gcry_sexp_build
+        (r_sexp, NULL,
+         "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))",
+         ec->p, ec->a, ec->b, mpi_G, ec->n, mpi_Q);
+    }
+  else
+    rc = GPG_ERR_BAD_CRYPT_CTX;
+
+ leave:
+  mpi_free (mpi_Q);
+  mpi_free (mpi_G);
+  return rc;
+}
 
 
 \f
@@ -1600,7 +2414,7 @@ selftests_ecdsa (selftest_report_func_t report)
 
  failed:
   if (report)
-    report ("pubkey", GCRY_PK_ECDSA, what, errtxt);
+    report ("pubkey", GCRY_PK_ECC, what, errtxt);
   return GPG_ERR_SELFTEST_FAILED;
 }
 
@@ -1609,72 +2423,33 @@ selftests_ecdsa (selftest_report_func_t report)
 static gpg_err_code_t
 run_selftests (int algo, int extended, selftest_report_func_t report)
 {
-  gpg_err_code_t ec;
-
   (void)extended;
 
-  switch (algo)
-    {
-    case GCRY_PK_ECDSA:
-      ec = selftests_ecdsa (report);
-      break;
-    default:
-      ec = GPG_ERR_PUBKEY_ALGO;
-      break;
+  if (algo != GCRY_PK_ECC)
+    return GPG_ERR_PUBKEY_ALGO;
 
-    }
-  return ec;
+  return selftests_ecdsa (report);
 }
 
 
 
 \f
-static const char *ecdsa_names[] =
-  {
-    "ecdsa",
-    "ecc",
-    NULL,
-  };
-static const char *ecdh_names[] =
+gcry_pk_spec_t _gcry_pubkey_spec_ecc =
   {
-    "ecdh",
-    "ecc",
-    NULL,
-  };
-
-gcry_pk_spec_t _gcry_pubkey_spec_ecdsa =
-  {
-    "ECDSA", ecdsa_names,
-    "pabgnq", "pabgnqd", "", "rs", "pabgnq",
-    GCRY_PK_USAGE_SIGN,
-    ecc_generate,
-    ecc_check_secret_key,
-    NULL,
-    NULL,
-    ecc_sign,
-    ecc_verify,
-    ecc_get_nbits
-  };
-
-gcry_pk_spec_t _gcry_pubkey_spec_ecdh =
-  {
-    "ECDH", ecdh_names,
-    "pabgnq", "pabgnqd", "", "rs", "pabgnq",
-    GCRY_PK_USAGE_ENCR,
+    GCRY_PK_ECC, { 0, 0 },
+    (GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR),
+    "ECC", ecc_names,
+    "pabgnq", "pabgnqd", "sw", "rs", "pabgnq",
     ecc_generate,
     ecc_check_secret_key,
     ecc_encrypt_raw,
     ecc_decrypt_raw,
-    NULL,
-    NULL,
-    ecc_get_nbits
-  };
-
-
-pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa =
-  {
+    ecc_sign,
+    ecc_verify,
+    ecc_get_nbits,
     run_selftests,
-    ecc_generate_ext,
     compute_keygrip,
-    ecc_get_param
+    _gcry_ecc_get_param,
+    _gcry_ecc_get_curve,
+    _gcry_ecc_get_param_sexp
   };