pubkey: Move sexp parsing of remaining fucntions to the modules.
[libgcrypt.git] / cipher / ecc.c
index 2161b64..bd4d253 100644 (file)
 #include "ecc-common.h"
 
 
+static const char *ecc_names[] =
+  {
+    "ecc",
+    "ecdsa",
+    "ecdh",
+    "eddsa",
+    NULL,
+  };
+
+
 /* Registered progress function and its callback value. */
 static void (*progress_cb) (void *, const char*, int, int, int);
 static void *progress_cb_data;
@@ -84,6 +94,7 @@ 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);
 
 
 
@@ -408,21 +419,16 @@ sign_ecdsa (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_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 = NULL;
           if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
@@ -458,11 +464,14 @@ sign_ecdsa (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, 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)
     {
@@ -606,24 +615,17 @@ eddsa_encodempi (gcry_mpi_t mpi, unsigned int minlen,
 }
 
 
-/* Encode POINT using the EdDSA scheme.  X and Y are scratch variables
-   supplied by the caller and CTX is the usual context.  MINLEN is the
-   required length in bytes for the result. 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.  */
+/* 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_encodepoint (mpi_point_t point, unsigned int minlen, mpi_ec_t ctx,
-                   gcry_mpi_t x, gcry_mpi_t y,
-                   unsigned char **r_buffer, unsigned int *r_buflen)
+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;
 
-  if (_gcry_mpi_ec_get_affine (x, y, point, ctx))
-    {
-      log_error ("eddsa_encodepoint: Failed to get affine coordinates\n");
-      return GPG_ERR_INTERNAL;
-    }
   rawmpi = _gcry_mpi_get_buffer (y, minlen, &rawmpilen, NULL);
   if (!rawmpi)
     return gpg_err_code_from_syserror ();
@@ -635,19 +637,49 @@ eddsa_encodepoint (mpi_point_t point, unsigned int minlen, mpi_ec_t ctx,
   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)
+{
+  gpg_err_code_t rc;
+  gcry_mpi_t x, y;
 
-/* Decode the EdDSA style encoded PK and set it into RESULT.  LEN is
-   the expected length in bytes of the encoded key and CTX 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 release by the caller.  In
-   contrast to the supplied PK, this is not an MPI and thus guarnteed
-   to be properly padded.  R_ENCPKLEN received the length of that
-   encoded key.  */
-static gpg_err_code_t
-eddsa_decodepoint (gcry_mpi_t pk, unsigned int len, mpi_ec_t ctx,
-                   mpi_point_t result,
-                   unsigned char **r_encpk, unsigned int *r_encpklen)
+  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))
+    {
+      log_error ("eddsa_encodepoint: Failed to get affine coordinates\n");
+      rc = GPG_ERR_INTERNAL;
+    }
+  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;
@@ -655,12 +687,50 @@ eddsa_decodepoint (gcry_mpi_t pk, unsigned int len, mpi_ec_t ctx,
 
   if (mpi_is_opaque (pk))
     {
-      const void *buf;
+      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 ();
@@ -669,7 +739,10 @@ eddsa_decodepoint (gcry_mpi_t pk, unsigned int len, mpi_ec_t ctx,
     }
   else
     {
-      rawmpi = _gcry_mpi_get_buffer (pk, len, &rawmpilen, NULL);
+      /* 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 ();
     }
@@ -845,10 +918,9 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
 {
   int rc;
   mpi_ec_t ctx = NULL;
-  int b = 256/8;             /* The only size we currently support.  */
+  int b;
   unsigned int tmp;
-  unsigned char hash_d[64];  /* Fixme: malloc in secure memory */
-  unsigned char digest[64];
+  unsigned char *digest;
   gcry_buffer_t hvec[3];
   const void *mbuf;
   size_t mlen;
@@ -876,39 +948,47 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
   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. */
 
-  /* Hash the secret key.  We clear DIGEST so we can use it to left
-     pad the key with zeroes for hashing.  */
+  digest = gcry_calloc_secure (2, b);
+  if (!digest)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+
+  /* 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;
     }
-  memset (digest, 0, b);
   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, hash_d, hvec, 2);
+  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 hash_d).  */
-  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);
-  /* log_printmpi ("     a", a); */
+  /* 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 = eddsa_decodepoint (pk, b, ctx, &Q,  &encpk, &encpklen);
+      rc = _gcry_ecc_eddsa_decodepoint (pk, ctx, &Q,  &encpk, &encpklen);
       if (rc)
         goto leave;
       if (DBG_CIPHER)
@@ -922,7 +1002,7 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
   else
     {
       _gcry_mpi_ec_mul_point (&Q, a, &skey->E.G, ctx);
-      rc = eddsa_encodepoint (&Q, b, ctx, x, y, &encpk, &encpklen);
+      rc = _gcry_ecc_eddsa_encodepoint (&Q, ctx, x, y, &encpk, &encpklen);
       if (rc)
         goto leave;
       if (DBG_CIPHER)
@@ -935,7 +1015,7 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
   if (DBG_CIPHER)
     log_printhex ("     m", mbuf, mlen);
 
-  hvec[0].data = hash_d;
+  hvec[0].data = digest;
   hvec[0].off  = 32;
   hvec[0].len  = 32;
   hvec[1].data = (char*)mbuf;
@@ -952,7 +1032,7 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
     log_printpnt ("   r", &I, ctx);
 
   /* Convert R into affine coordinates and apply encoding.  */
-  rc = eddsa_encodepoint (&I, b, ctx, x, y, &rawmpi, &rawmpilen);
+  rc = _gcry_ecc_eddsa_encodepoint (&I, ctx, x, y, &rawmpi, &rawmpilen);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -997,6 +1077,7 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
   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);
@@ -1016,7 +1097,7 @@ verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
 {
   int rc;
   mpi_ec_t ctx = NULL;
-  int b = 256/8;               /* The only size we currently support.  */
+  int b;
   unsigned int tmp;
   mpi_point_struct Q;          /* Public key.  */
   unsigned char *encpk = NULL; /* Encoded public key.  */
@@ -1043,9 +1124,12 @@ verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
 
   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 = eddsa_decodepoint (pk, b, ctx, &Q, &encpk, &encpklen);
+  rc = _gcry_ecc_eddsa_decodepoint (pk, ctx, &Q, &encpk, &encpklen);
   if (rc)
     goto leave;
   if (!_gcry_mpi_ec_curve_point (&Q, ctx))
@@ -1119,7 +1203,7 @@ verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
   _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 = eddsa_encodepoint (&Ia, b, ctx, s, h, &tbuf, &tlen);
+  rc = _gcry_ecc_eddsa_encodepoint (&Ia, ctx, s, h, &tbuf, &tlen);
   if (rc)
     goto leave;
   if (tlen != rlen || memcmp (tbuf, rbuf, tlen))
@@ -1153,10 +1237,10 @@ verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
  *********************************************/
 
 static gcry_err_code_t
-ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
-              const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
+ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
 {
   gpg_err_code_t rc;
+  unsigned int nbits;
   elliptic_curve_t E;
   ECC_secret_key sk;
   gcry_mpi_t x = NULL;
@@ -1171,31 +1255,29 @@ ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
   gcry_mpi_t public = NULL;
   gcry_mpi_t secret = NULL;
 
-  (void)algo;
-  (void)evalue;
-
   memset (&E, 0, sizeof E);
   memset (&sk, 0, sizeof sk);
 
-  if (genparms)
+  rc = _gcry_pk_util_get_nbits (genparms, &nbits);
+  if (rc)
+    return rc;
+
+  /* Parse the optional "curve" parameter. */
+  l1 = gcry_sexp_find_token (genparms, "curve", 0);
+  if (l1)
     {
-      /* 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. */
-        }
+      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. */
+    }
 
-      /* Parse the optional transient-key flag.  */
-      l1 = gcry_sexp_find_token (genparms, "transient-key", 0);
-      if (l1)
-        {
-          transient_key = 1;
-          gcry_sexp_release (l1);
-        }
+  /* Parse the optional transient-key flag.  */
+  l1 = gcry_sexp_find_token (genparms, "transient-key", 0);
+  if (l1)
+    {
+      transient_key = 1;
+      gcry_sexp_release (l1);
     }
 
   /* NBITS is required if no curve name has been given.  */
@@ -1250,7 +1332,7 @@ ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
       unsigned char *encpk;
       unsigned int encpklen;
 
-      rc = eddsa_encodepoint (&sk.Q, 256/8, ctx, x, y, &encpk, &encpklen);
+      rc = _gcry_ecc_eddsa_encodepoint (&sk.Q, ctx, x, y, &encpk, &encpklen);
       if (rc)
         return rc;
       public = mpi_new (0);
@@ -1315,200 +1397,362 @@ ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
 
 
 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;
+  memset (&sk, 0, sizeof sk);
 
-  sk.E.model = MPI_EC_WEIERSTRASS;
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  err = _gcry_ecc_os2ec (&sk.E.G, skey[3]);
-  if (err)
+  /*
+   * 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 = _gcry_ecc_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;
     }
 
-  {
-    const unsigned char *buf;
-    unsigned int n;
-
-    gcry_assert (mpi_is_opaque (skey[6]));
+  if (mpi_q)
+    {
+      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;
+    }
 
-    buf = gcry_mpi_get_opaque (skey[6], &n);
-    if (!buf)
-      err = GPG_ERR_INV_OBJ;
-    else
-      {
-        n = (n + 7)/8;
-        sk.d = NULL;
-        err = gcry_mpi_scan (&sk.d, GCRYMPI_FMT_USG, buf, n, NULL);
-        if (!err)
-          {
-            if (check_secret_key (&sk))
-              err = GPG_ERR_BAD_SECKEY;
-            gcry_mpi_release (sk.d);
-            sk.d = NULL;
-          }
-      }
-  }
+  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 err;
+  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_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey,
-          int flags, int hashalgo)
+ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
 {
-  gpg_err_code_t rc;
+  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 r, s;
-
-  (void)algo;
-
-  if (!data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
-      || !skey[6] )
-    return GPG_ERR_BAD_MPI;
-
-  sk.E.model = ((flags & PUBKEY_FLAG_EDDSA)
-                ? MPI_EC_TWISTEDEDWARDS
-                : MPI_EC_WEIERSTRASS);
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  sk.Q.x = NULL;
-  sk.Q.y = NULL;
-  sk.Q.z = NULL;
-  rc = _gcry_ecc_os2ec (&sk.E.G, skey[3]);
+  gcry_mpi_t sig_r = NULL;
+  gcry_mpi_t sig_s = NULL;
+
+  memset (&sk, 0, sizeof sk);
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, 0);
+
+  /* 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_free (&sk.E.G);
-      return rc;
+      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;
+        }
+    }
+  /* 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)
+    {
+      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.E.n = skey[4];
-
-  r = mpi_alloc (mpi_get_nlimbs (sk.E.p));
-  s = mpi_alloc (mpi_get_nlimbs (sk.E.p));
 
-  {
-    const unsigned char *buf;
-    unsigned int n;
 
-    gcry_assert (mpi_is_opaque (skey[6]));
+  sig_r = gcry_mpi_new (0);
+  sig_s = gcry_mpi_new (0);
+  if ((ctx.flags & PUBKEY_FLAG_EDDSA))
+    {
+      /* 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);
+    }
 
-    buf = gcry_mpi_get_opaque (skey[6], &n);
-    if (!buf)
-      rc = GPG_ERR_INV_OBJ;
-    else
-      {
-        n = (n + 7)/8;
-        sk.d = NULL;
-        rc = gcry_mpi_scan (&sk.d, GCRYMPI_FMT_USG, buf, n, NULL);
-        if (!rc)
-          {
-            if ((flags & PUBKEY_FLAG_EDDSA))
-              {
-                rc = sign_eddsa (data, &sk, r, s, hashalgo, skey[5]);
-                if (!rc)
-                  rc = gcry_sexp_build (r_result, NULL,
-                                        "(sig-val(eddsa(r%M)(s%M)))", r, s);
-              }
-            else
-              {
-                rc = sign_ecdsa (data, &sk, r, s, flags, hashalgo);
-                if (!rc)
-                  rc = gcry_sexp_build (r_result, NULL,
-                                        "(sig-val(ecdsa(r%M)(s%M)))", r, s);
-              }
-            gcry_mpi_release (sk.d);
-            sk.d = NULL;
-          }
-      }
-  }
 
-  mpi_free (r);
-  mpi_free (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);
-  if (sk.Q.x)
-    point_free (&sk.Q);
+  gcry_mpi_release (sk.E.n);
+  gcry_mpi_release (mpi_q);
+  point_free (&sk.Q);
+  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,
-            int flags, int hashalgo)
+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;
+
+  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);
 
-  (void)algo;
-  (void)cmp;
-  (void)opaquev;
+  /*
+   * 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.model = ((flags & PUBKEY_FLAG_EDDSA)
-                ? MPI_EC_TWISTEDEDWARDS
-                : MPI_EC_WEIERSTRASS);
-  pk.E.p = pkey[0];
-  pk.E.a = pkey[1];
-  pk.E.b = pkey[2];
-  point_init (&pk.E.G);
-  err = _gcry_ecc_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_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 (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)
     {
-      point_free (&pk.E.G);
-      return err;
+      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);
     }
-  pk.E.n = pkey[4];
 
-  if ((flags & PUBKEY_FLAG_EDDSA))
+  if (DBG_CIPHER)
+    {
+      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)
     {
-      pk.Q.x = NULL;
-      pk.Q.y = NULL;
-      pk.Q.z = NULL;
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
 
-      err = verify_eddsa (hash, &pk, data[0], data[1], hashalgo, pkey[5]);
+
+  /*
+   * 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);
-      err = _gcry_ecc_os2ec (&pk.Q, pkey[5]);
-      if (err)
-        {
-          point_free (&pk.E.G);
-          point_free (&pk.Q);
-          return err;
-        }
+      rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
+      if (rc)
+        goto leave;
 
-      if (mpi_is_opaque (hash))
+      if (mpi_is_opaque (data))
         {
           const void *abuf;
           unsigned int abits, qbits;
@@ -1516,24 +1760,40 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
 
           qbits = mpi_get_nbits (pk.E.n);
 
-          abuf = gcry_mpi_get_opaque (hash, &abits);
-          err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, abuf, (abits+7)/8, NULL);
-          if (!err)
+          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);
 
-              err = verify_ecdsa (a, &pk, data[0], data[1]);
+              rc = verify_ecdsa (a, &pk, sig_r, sig_s);
               gcry_mpi_release (a);
             }
         }
       else
-        err = verify_ecdsa (hash, &pk, data[0], data[1]);
+        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;
 }
 
 
@@ -1566,46 +1826,106 @@ 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_sexp_t *r_result, 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)
 {
-  gpg_err_code_t rc;
+  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 s, e;
-
-  (void)algo;
-  (void)flags;
+  mpi_ec_t ec = NULL;
 
-  if (!k
-      || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4] || !pkey[5])
-    return GPG_ERR_BAD_MPI;
+  memset (&pk, 0, sizeof pk);
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
+                                   ecc_get_nbits (keyparms));
 
-  pk.E.model = MPI_EC_WEIERSTRASS;
-  pk.E.p = pkey[0];
-  pk.E.a = pkey[1];
-  pk.E.b = pkey[2];
-  point_init (&pk.E.G);
-  rc = _gcry_ecc_os2ec (&pk.E.G, pkey[3]);
+  /*
+   * 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))
     {
-      point_free (&pk.E.G);
-      return rc;
+      rc = GPG_ERR_INV_DATA;
+      goto leave;
     }
-  pk.E.n = pkey[4];
-  point_init (&pk.Q);
-  rc = _gcry_ecc_os2ec (&pk.Q, pkey[5]);
+
+
+  /*
+   * 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_free (&pk.E.G);
-      point_free (&pk.Q);
-      return rc;
+      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;
+    }
+
+  if (DBG_CIPHER)
+    {
+      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);
+    }
+  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;
     }
 
-  ctx = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect,
-                                     pk.E.p, pk.E.a, pk.E.b);
-  s = mpi_alloc (mpi_get_nlimbs (pk.E.p));
-  e = mpi_alloc (mpi_get_nlimbs (pk.E.p));
+  /* Convert the public key.  */
+  if (mpi_q)
+    {
+      point_init (&pk.Q);
+      rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
+      if (rc)
+        goto leave;
+    }
+
+  /* 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 */
   {
@@ -1618,18 +1938,18 @@ ecc_encrypt_raw (int algo, gcry_sexp_t *r_result, 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");
-    s = _gcry_ecc_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");
-    e = _gcry_ecc_ec2os (x, y, pk.E.p);
+    mpi_e = _gcry_ecc_ec2os (x, y, pk.E.p);
 
     mpi_free (x);
     mpi_free (y);
@@ -1637,17 +1957,30 @@ ecc_encrypt_raw (int algo, gcry_sexp_t *r_result, gcry_mpi_t k,
     point_free (&R);
   }
 
-  _gcry_mpi_ec_free (ctx);
+  rc = gcry_sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))",
+                        mpi_s, mpi_e);
+
+ 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);
-
-  rc = gcry_sexp_build (r_result, NULL, "(enc-val(ecdh(s%m)(e%m)))", s, e);
-  mpi_free (s);
-  mpi_free (e);
-
+  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:
@@ -1656,70 +1989,115 @@ ecc_encrypt_raw (int algo, gcry_sexp_t *r_result, gcry_mpi_t k,
  *  see ecc_encrypt_raw for details.
  */
 static gcry_err_code_t
-ecc_decrypt_raw (int algo, gcry_sexp_t *r_plain, gcry_mpi_t *data,
-                 gcry_mpi_t *skey, int flags,
-                 enum pk_encoding encoding, int hash_algo,
-                 unsigned char *label, size_t labellen)
+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_struct R;  /* Result that we return.  */
+  gcry_mpi_t mpi_g = NULL;
+  char *curvename = NULL;
+  mpi_ec_t ec = NULL;
   mpi_point_struct kG;
-  mpi_ec_t ctx;
-  gcry_mpi_t r;
+  mpi_point_struct R;
+  gcry_mpi_t r = NULL;
 
-  (void)algo;
-  (void)flags;
-  (void)encoding;
-  (void)hash_algo;
-  (void)label;
-  (void)labellen;
+  memset (&sk, 0, sizeof sk);
+  point_init (&kG);
+  point_init (&R);
 
-  if (!data || !data[0]
-      || !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_DECRYPT,
+                                   ecc_get_nbits (keyparms));
 
-  point_init (&kG);
-  rc = _gcry_ecc_os2ec (&kG, data[0]);
+  /*
+   * 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 rc;
+      rc = GPG_ERR_INV_DATA;
+      goto leave;
     }
 
-  sk.E.model = MPI_EC_WEIERSTRASS;
-  sk.E.p = skey[0];
-  sk.E.a = skey[1];
-  sk.E.b = skey[2];
-  point_init (&sk.E.G);
-  rc = _gcry_ecc_os2ec (&sk.E.G, skey[3]);
+  /*
+   * 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 rc;
+      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;
+        }
+    }
+  /* 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;
     }
-  sk.E.n = skey[4];
-  point_init (&sk.Q);
-  rc = _gcry_ecc_os2ec (&sk.Q, skey[5]);
+
+
+  /*
+   * 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 rc;
     }
-  sk.d = skey[6];
 
-  ctx = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect,
-                                     sk.E.p, sk.E.a, sk.E.b);
+  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:  */
   {
@@ -1728,7 +2106,7 @@ ecc_decrypt_raw (int algo, gcry_sexp_t *r_plain, 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 = _gcry_ecc_ec2os (x, y, sk.E.p);
@@ -1739,26 +2117,84 @@ ecc_decrypt_raw (int algo, gcry_sexp_t *r_plain, gcry_mpi_t *data,
     mpi_free (x);
     mpi_free (y);
   }
+  if (DBG_CIPHER)
+    log_printmpi ("ecc_decrypt  res", r);
 
+  if (!rc)
+    rc = gcry_sexp_build (r_plain, NULL, "(value %m)", r);
+
+ leave:
   point_free (&R);
-  _gcry_mpi_ec_free (ctx);
   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);
-  point_free (&sk.Q);
-
-  if (!rc)
-    rc = gcry_sexp_build (r_plain, NULL, "(value %m)", r);
-  mpi_free (r);
+  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.  */
+
+      curve = _gcry_sexp_nth_string (l1, 1);
+      gcry_sexp_release (l1);
+      if (!curve)
+        return 0;  /* No curve name given (or out of core). */
 
-  return mpi_get_nbits (pkey[0]);
+      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;
 }
 
 
@@ -1871,7 +2307,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
    Low-level API helper functions.
  */
 
-/* This is the wroker function for gcry_pubkey_get_sexp for ECC
+/* 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
@@ -1889,10 +2325,7 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
 
   /* Compute the public point if it is missing.  */
   if (!ec->Q && ec->d)
-    {
-      ec->Q = gcry_mpi_point_new (0);
-      _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
-    }
+    ec->Q = _gcry_ecc_compute_public (NULL, ec);
 
   /* Encode G and Q.  */
   mpi_G = _gcry_mpi_ec_ec2os (ec->G, ec);
@@ -1906,7 +2339,23 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
       rc = GPG_ERR_BAD_CRYPT_CTX;
       goto leave;
     }
-  mpi_Q = _gcry_mpi_ec_ec2os (ec->Q, ec);
+
+  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;
@@ -1965,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;
 }
 
@@ -1974,72 +2423,29 @@ 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",
-    "eddsa",
-    "ecc",
-    NULL,
-  };
-static const char *ecdh_names[] =
-  {
-    "ecdh",
-    "ecc",
-    NULL,
-  };
-
-gcry_pk_spec_t _gcry_pubkey_spec_ecdsa =
-  {
-    GCRY_PK_ECDSA, { 0, 0 },
-    GCRY_PK_USAGE_SIGN,
-    "ECDSA", ecdsa_names,
-    "pabgnq", "pabgnqd", "", "rs", "pabgnq",
-    ecc_generate,
-    ecc_check_secret_key,
-    NULL,
-    NULL,
-    ecc_sign,
-    ecc_verify,
-    ecc_get_nbits,
-    run_selftests,
-    compute_keygrip,
-    _gcry_ecc_get_param,
-    _gcry_ecc_get_curve,
-    _gcry_ecc_get_param_sexp
-  };
-
-gcry_pk_spec_t _gcry_pubkey_spec_ecdh =
+gcry_pk_spec_t _gcry_pubkey_spec_ecc =
   {
-    GCRY_PK_ECDH, { 0, 0 },
-    GCRY_PK_USAGE_ENCR,
-    "ECDH", ecdh_names,
-    "pabgnq", "pabgnqd", "se", "", "pabgnq",
+    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_sign,
+    ecc_verify,
     ecc_get_nbits,
     run_selftests,
     compute_keygrip,