ecc: Allow the name "q@eddsa" to get/set the public key.
authorWerner Koch <wk@gnupg.org>
Sat, 7 Sep 2013 08:06:46 +0000 (10:06 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 25 Sep 2013 15:06:05 +0000 (17:06 +0200)
* cipher/ecc-curves.c (_gcry_ecc_get_mpi): Support "q@eddsa".
(_gcry_ecc_set_mpi): Support "q".
* cipher/ecc.c (eddsa_encodepoint): Rename to ...
(_gcry_ecc_eddsa_encodepoint): this and make global.  Remove arg
MINLEN and take from context.
(eddsa_decodepoint): Rename to
(_gcry_ecc_eddsa_decodepoint): this and make global. Remove arg LEN
and take from context.
(sign_eddsa, verify_eddsa): Take B from context.
(ecc_sign, ecc_verify): Add hack to set DIALECT.
(_gcry_pk_ecc_get_sexp): Use _gcry_ecc_compute_public.  Handle EdDSA.
* src/ec-context.h (mpi_ec_ctx_s): Add field NBITS.
* mpi/ec.c (ec_p_init): Init NBITS.
* tests/t-mpi-point.c (test_curve): Add Ed25519.
(sample_ed25519_q): New.
(context_param): Check new sample key.
(hex2buffer, hex2mpiopa): New.
(cmp_mpihex): Take care of opaque MPIs.

Signed-off-by: Werner Koch <wk@gnupg.org>
cipher/ecc-common.h
cipher/ecc-curves.c
cipher/ecc.c
doc/gcrypt.texi
mpi/ec.c
src/ec-context.h
src/gcrypt.h.in
tests/t-mpi-point.c

index 031994a..cdb1362 100644 (file)
@@ -84,5 +84,15 @@ gcry_error_t _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value);
 
 mpi_point_t  _gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec);
 
+/*-- ecc.c --*/
+gpg_err_code_t _gcry_ecc_eddsa_encodepoint (mpi_point_t point, mpi_ec_t ctx,
+                                            gcry_mpi_t x, gcry_mpi_t y,
+                                            unsigned char **r_buffer,
+                                            unsigned int *r_buflen);
+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);
+
 
 #endif /*GCRY_ECC_COMMON_H*/
index e6a993f..7447340 100644 (file)
@@ -812,6 +812,9 @@ _gcry_ecc_get_param_sexp (const char *name)
 gcry_mpi_t
 _gcry_ecc_get_mpi (const char *name, mpi_ec_t ec, int copy)
 {
+  if (!*name)
+    return NULL;
+
   if (!strcmp (name, "p") && ec->p)
     return mpi_is_const (ec->p) && !copy? ec->p : mpi_copy (ec->p);
   if (!strcmp (name, "a") && ec->a)
@@ -833,17 +836,35 @@ _gcry_ecc_get_mpi (const char *name, mpi_ec_t ec, int copy)
   if (!strcmp (name, "q.y") && ec->Q && ec->Q->y)
     return mpi_is_const (ec->G->y) && !copy? ec->Q->y : mpi_copy (ec->Q->y);
 
-  /* If a point has been requested, return it in standard encoding.  */
+  /* If the base point has been requested, return it in standard
+     encoding.  */
   if (!strcmp (name, "g") && ec->G)
     return _gcry_mpi_ec_ec2os (ec->G, ec);
-  if (!strcmp (name, "q"))
+
+  /* If the public key has been requested, return it by default in
+     standard uncompressed encoding or if requested in other
+     encodings.  */
+  if (*name == 'q' && (!name[1] || name[1] == '@'))
     {
       /* If only the private key is given, compute the public key.  */
       if (!ec->Q)
         ec->Q = _gcry_ecc_compute_public (NULL, ec);
 
-      if (ec->Q)
+      if (!ec->Q)
+        return NULL;
+
+      if (name[1] != '@')
         return _gcry_mpi_ec_ec2os (ec->Q, ec);
+
+      if (!strcmp (name+2, "eddsa") && ec->model == MPI_EC_TWISTEDEDWARDS)
+        {
+          unsigned char *encpk;
+          unsigned int encpklen;
+
+          if (!_gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL,
+                                            &encpk, &encpklen))
+            return gcry_mpi_set_opaque (NULL, encpk, encpklen*8);
+        }
     }
 
   return NULL;
@@ -874,7 +895,11 @@ _gcry_ecc_get_point (const char *name, mpi_ec_t ec)
 gpg_err_code_t
 _gcry_ecc_set_mpi (const char *name, gcry_mpi_t newvalue, mpi_ec_t ec)
 {
-  if (!strcmp (name, "p"))
+  gpg_err_code_t rc = 0;
+
+  if (!*name)
+    ;
+  else if (!strcmp (name, "p"))
     {
       mpi_free (ec->p);
       ec->p = mpi_copy (newvalue);
@@ -896,15 +921,40 @@ _gcry_ecc_set_mpi (const char *name, gcry_mpi_t newvalue, mpi_ec_t ec)
       mpi_free (ec->n);
       ec->n = mpi_copy (newvalue);
     }
+  else if (*name == 'q' && (!name[1] || name[1] == '@'))
+    {
+      if (newvalue)
+        {
+          if (!ec->Q)
+            ec->Q = gcry_mpi_point_new (0);
+          if (ec->dialect == ECC_DIALECT_ED25519)
+            rc = _gcry_ecc_eddsa_decodepoint (newvalue, ec, ec->Q, NULL, NULL);
+          else
+            rc = _gcry_ecc_os2ec (ec->Q, newvalue);
+        }
+      if (rc || !newvalue)
+        {
+          gcry_mpi_point_release (ec->Q);
+          ec->Q = NULL;
+        }
+      /* Note: We assume that Q matches d and thus do not reset d.  */
+    }
   else if (!strcmp (name, "d"))
     {
       mpi_free (ec->d);
       ec->d = mpi_copy (newvalue);
+      if (ec->d)
+        {
+          /* We need to reset the public key because it may not
+             anymore match.  */
+          gcry_mpi_point_release (ec->Q);
+          ec->Q = NULL;
+        }
     }
   else
-    return GPG_ERR_UNKNOWN_NAME;
+   rc = GPG_ERR_UNKNOWN_NAME;
 
-  return 0;
+  return rc;
 }
 
 
index 0b89ec2..abd501f 100644 (file)
@@ -626,36 +626,47 @@ eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int minlen,
   return 0;
 }
 
-/* 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
-   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)
+/* 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)
 {
-  if (_gcry_mpi_ec_get_affine (x, y, point, ctx))
+  gpg_err_code_t rc;
+  gcry_mpi_t x, y;
+
+  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");
-      return GPG_ERR_INTERNAL;
+      rc = GPG_ERR_INTERNAL;
     }
-  return eddsa_encode_x_y (x, y, minlen, r_buffer, r_buflen);
+  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.  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 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.  */
-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)
+/* 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;
@@ -694,7 +705,7 @@ eddsa_decodepoint (gcry_mpi_t pk, unsigned int len, mpi_ec_t ctx,
 
           if (r_encpk)
             {
-              rc = eddsa_encode_x_y (x, y, len, r_encpk, r_encpklen);
+              rc = eddsa_encode_x_y (x, y, ctx->nbits/8, r_encpk, r_encpklen);
               if (rc)
                 {
                   mpi_free (x);
@@ -720,7 +731,7 @@ eddsa_decodepoint (gcry_mpi_t pk, unsigned int len, mpi_ec_t ctx,
       /* 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, len, &rawmpilen, NULL);
+      rawmpi = _gcry_mpi_get_buffer (pk, ctx->nbits/8, &rawmpilen, NULL);
       if (!rawmpi)
         return gpg_err_code_from_syserror ();
     }
@@ -896,7 +907,7 @@ 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];
@@ -927,6 +938,10 @@ 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/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.  */
@@ -959,7 +974,7 @@ sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
      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)
@@ -973,7 +988,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)
@@ -1003,7 +1018,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)
@@ -1067,7 +1082,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.  */
@@ -1094,9 +1109,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))
@@ -1170,7 +1188,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))
@@ -1301,7 +1319,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);
@@ -1443,9 +1461,15 @@ ecc_sign (int algo, gcry_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey,
       || !skey[6] )
     return GPG_ERR_BAD_MPI;
 
+  /* FIXME: The setting of model and dialect are crude hacks.  We will
+     fix that by moving the s-expression parsing from pubkey.c to
+     here.  */
   sk.E.model = ((flags & PUBKEY_FLAG_EDDSA)
                 ? MPI_EC_TWISTEDEDWARDS
                 : MPI_EC_WEIERSTRASS);
+  sk.E.dialect = ((flags & PUBKEY_FLAG_EDDSA)
+                  ? ECC_DIALECT_ED25519
+                  : ECC_DIALECT_STANDARD);
   sk.E.p = skey[0];
   sk.E.a = skey[1];
   sk.E.b = skey[2];
@@ -1525,9 +1549,15 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
       || !pkey[3] || !pkey[4] || !pkey[5] )
     return GPG_ERR_BAD_MPI;
 
+  /* FIXME: The setting of model and dialect are crude hacks.  We will
+     fix that by moving the s-expression parsing from pubkey.c to
+     here.  */
   pk.E.model = ((flags & PUBKEY_FLAG_EDDSA)
                 ? MPI_EC_TWISTEDEDWARDS
                 : MPI_EC_WEIERSTRASS);
+  pk.E.dialect = ((flags & PUBKEY_FLAG_EDDSA)
+                  ? ECC_DIALECT_ED25519
+                  : ECC_DIALECT_STANDARD);
   pk.E.p = pkey[0];
   pk.E.a = pkey[1];
   pk.E.b = pkey[2];
@@ -1922,7 +1952,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
@@ -1940,10 +1970,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);
@@ -1957,7 +1984,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;
index 5d1be8d..0590a26 100644 (file)
@@ -4182,10 +4182,16 @@ modified, it is suggested to pass @code{1} to @var{copy}, so that the
 function guarantees that a modifiable copy of the MPI is returned.  If
 @code{0} is used for @var{copy}, this function may return a constant
 flagged MPI.  In any case @code{gcry_mpi_release} needs to be called
-to release the result.  For valid names @ref{ecc_keyparam}.  If a
-point parameter is requested it is returned as an uncompressed encoded
-point.  If the public key @code{q} is requested but only the private
-key @code{d} is available, @code{q} will be recomputed on the fly.
+to release the result.  For valid names @ref{ecc_keyparam}.  If the
+public key @code{q} is requested but only the private key @code{d} is
+available, @code{q} will be recomputed on the fly.  If a point
+parameter is requested it is returned as an uncompressed
+encoded point unless these special names are used:
+@table @var
+@item q@@eddsa
+Return an EdDSA style compressed point.  This is only supported for
+Twisted Edwards curves.
+@end table
 @end deftypefun
 
 @deftypefun gcry_mpi_point_t gcry_mpi_ec_get_point ( @
index 883d8f6..c6d0fc8 100644 (file)
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -436,6 +436,10 @@ ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model,
 
   ctx->model = model;
   ctx->dialect = dialect;
+  if (dialect == ECC_DIALECT_ED25519)
+    ctx->nbits = 256;
+  else
+    ctx->nbits = mpi_get_nbits (p);
   ctx->p = mpi_copy (p);
   ctx->a = mpi_copy (a);
   if (b && model == MPI_EC_TWISTEDEDWARDS)
index fdfbc0a..8dce7a7 100644 (file)
@@ -27,6 +27,8 @@ struct mpi_ec_ctx_s
 
   enum ecc_dialects dialect;     /* The ECC dialect used with the curve.  */
 
+  unsigned int nbits;            /* Number of bits.  */
+
   /* Domain parameters.  Note that they may not all be set and if set
      the MPIs may be flaged as constant. */
   gcry_mpi_t p;         /* Prime specifying the field GF(p).  */
index 62c9721..5f49edc 100644 (file)
@@ -726,7 +726,7 @@ void gcry_mpi_set_flag (gcry_mpi_t a, enum gcry_mpi_flag flag);
    currently useless as no flags are allowed. */
 void gcry_mpi_clear_flag (gcry_mpi_t a, enum gcry_mpi_flag flag);
 
-/* Return true when the FLAG is set for A. */
+/* Return true if the FLAG is set for A. */
 int gcry_mpi_get_flag (gcry_mpi_t a, enum gcry_mpi_flag flag);
 
 /* Private function - do not use.  */
index 6683189..0641779 100644 (file)
@@ -36,6 +36,14 @@ static int debug;
 static int error_count;
 
 
+#define my_isascii(c) (!((c) & 0x80))
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
 #define xmalloc(a)    gcry_xmalloc ((a))
 #define xcalloc(a,b)  gcry_xcalloc ((a),(b))
 #define xfree(a)      gcry_free ((a))
@@ -113,6 +121,15 @@ static struct
       "0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6"
       "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650"
     },
+    {
+      "Ed25519",
+      "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
+      "-0x01",
+      "-0x98412DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235EC8FEDA4",
+      "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
+      "0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A",
+      "0x6666666666666666666666666666666666666666666666666666666666666658"
+    },
     { NULL, NULL, NULL, NULL, NULL }
   };
 
@@ -127,6 +144,20 @@ static const char sample_p256_q_y[] =
   "00E86525E38CCECFF3FB8D152CC6334F70D23A525175C1BCBDDE6E023B2228770E";
 
 
+/* A sample public key for Ed25519.  */
+static const char sample_ed25519_q[] =
+  "04"
+  "55d0e09a2b9d34292297e08d60d0f620c513d47253187c24b12786bd777645ce"
+  "1a5107f7681a02af2523a6daf372e10e3a0764c9d3fe4bd5b70ab18201985ad7";
+static const char sample_ed25519_q_x[] =
+  "55d0e09a2b9d34292297e08d60d0f620c513d47253187c24b12786bd777645ce";
+static const char sample_ed25519_q_y[] =
+  "1a5107f7681a02af2523a6daf372e10e3a0764c9d3fe4bd5b70ab18201985ad7";
+static const char sample_ed25519_q_eddsa[] =
+  "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
+static const char sample_ed25519_d[] =
+  "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60";
+
 
 static void
 show (const char *format, ...)
@@ -241,7 +272,49 @@ hex2mpi (const char *string)
 
   err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
   if (err)
-    die ("hex2mpi '%s' failed: %s\n", gpg_strerror (err));
+    die ("hex2mpi '%s' failed: %s\n", string, gpg_strerror (err));
+  return val;
+}
+
+
+/* Convert STRING consisting of hex characters into its binary
+   representation and return it as an allocated buffer. The valid
+   length of the buffer is returned at R_LENGTH.  The string is
+   delimited by end of string.  The function returns NULL on
+   error.  */
+static void *
+hex2buffer (const char *string, size_t *r_length)
+{
+  const char *s;
+  unsigned char *buffer;
+  size_t length;
+
+  buffer = xmalloc (strlen(string)/2+1);
+  length = 0;
+  for (s=string; *s; s +=2 )
+    {
+      if (!hexdigitp (s) || !hexdigitp (s+1))
+        return NULL;           /* Invalid hex digits. */
+      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+    }
+  *r_length = length;
+  return buffer;
+}
+
+
+static gcry_mpi_t
+hex2mpiopa (const char *string)
+{
+  char *buffer;
+  size_t buflen;
+  gcry_mpi_t val;
+
+  buffer = hex2buffer (string, &buflen);
+  if (!buffer)
+    die ("hex2mpiopa '%s' failed: parser error\n", string);
+  val = gcry_mpi_set_opaque (NULL, buffer, buflen*8);
+  if (!buffer)
+    die ("hex2mpiopa '%s' failed: set_opaque error%s\n", string);
   return val;
 }
 
@@ -253,7 +326,10 @@ cmp_mpihex (gcry_mpi_t a, const char *b)
   gcry_mpi_t bval;
   int res;
 
-  bval = hex2mpi (b);
+  if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+    bval = hex2mpiopa (b);
+  else
+    bval = hex2mpi (b);
   res = gcry_mpi_cmp (a, bval);
   gcry_mpi_release (bval);
   return res;
@@ -459,7 +535,7 @@ context_param (void)
   gpg_error_t err;
   int idx;
   gcry_ctx_t ctx = NULL;
-  gcry_mpi_t q;
+  gcry_mpi_t q, d;
   gcry_sexp_t keyparam;
 
   wherestr = "context_param";
@@ -492,13 +568,11 @@ context_param (void)
         continue;
 
     }
-  gcry_ctx_release (ctx);
-
 
-  show ("checking sample public key\n");
+  show ("checking sample public key (nistp256)\n");
   q = hex2mpi (sample_p256_q);
   err = gcry_sexp_build (&keyparam, NULL,
-                        "(public-key(ecdsa(curve %s)(q %m)))",
+                        "(public-key(ecc(curve %s)(q %m)))",
                         "NIST P-256", q);
   if (err)
     die ("gcry_sexp_build failed: %s\n", gpg_strerror (err));
@@ -511,18 +585,112 @@ context_param (void)
   /*   fail ("gcry_pk_testkey failed for sample public key: %s\n", */
   /*         gpg_strerror (err)); */
 
+  gcry_ctx_release (ctx);
   err = gcry_mpi_ec_new (&ctx, keyparam, NULL);
   if (err)
-    fail ("gcry_mpi_ec_new failed for sample public key: %s\n",
+    fail ("gcry_mpi_ec_new failed for sample public key (nistp256): %s\n",
           gpg_strerror (err));
   else
     {
       gcry_sexp_t sexp;
 
-      get_and_cmp_mpi ("q", sample_p256_q, "NIST P-256", ctx);
-      get_and_cmp_point ("q", sample_p256_q_x, sample_p256_q_y, "NIST P-256",
+      get_and_cmp_mpi ("q", sample_p256_q, "nistp256", ctx);
+      get_and_cmp_point ("q", sample_p256_q_x, sample_p256_q_y, "nistp256",
                          ctx);
 
+      /* Delete Q.  */
+      err = gcry_mpi_ec_set_mpi ("q", NULL, ctx);
+      if (err)
+        fail ("clearing Q for nistp256 failed: %s\n", gpg_strerror (err));
+      if (gcry_mpi_ec_get_mpi ("q", ctx, 0))
+        fail ("clearing Q for nistp256 did not work\n");
+
+      /* Set Q again.  */
+      q = hex2mpi (sample_p256_q);
+      err = gcry_mpi_ec_set_mpi ("q", q, ctx);
+      if (err)
+        fail ("setting Q for nistp256 failed: %s\n", gpg_strerror (err));
+      get_and_cmp_mpi ("q", sample_p256_q, "nistp256(2)", ctx);
+
+      /* Get as s-expression.  */
+      err = gcry_pubkey_get_sexp (&sexp, 0, ctx);
+      if (err)
+        fail ("gcry_pubkey_get_sexp(0) failed: %s\n", gpg_strerror (err));
+      else if (debug)
+        print_sexp ("Result of gcry_pubkey_get_sexp (0):\n", sexp);
+      gcry_sexp_release (sexp);
+
+      err = gcry_pubkey_get_sexp (&sexp, GCRY_PK_GET_PUBKEY, ctx);
+      if (err)
+        fail ("gcry_pubkey_get_sexp(GET_PUBKEY) failed: %s\n",
+              gpg_strerror (err));
+      else if (debug)
+        print_sexp ("Result of gcry_pubkey_get_sexp (GET_PUBKEY):\n", sexp);
+      gcry_sexp_release (sexp);
+
+      err = gcry_pubkey_get_sexp (&sexp, GCRY_PK_GET_SECKEY, ctx);
+      if (gpg_err_code (err) != GPG_ERR_NO_SECKEY)
+        fail ("gcry_pubkey_get_sexp(GET_SECKEY) returned wrong error: %s\n",
+              gpg_strerror (err));
+      gcry_sexp_release (sexp);
+    }
+
+  show ("checking sample public key (Ed25519)\n");
+  q = hex2mpi (sample_ed25519_q);
+  gcry_sexp_release (keyparam);
+  err = gcry_sexp_build (&keyparam, NULL,
+                        "(public-key(ecc(curve %s)(q %m)))",
+                        "Ed25519", q);
+  if (err)
+    die ("gcry_sexp_build failed: %s\n", gpg_strerror (err));
+  gcry_mpi_release (q);
+
+  /* We can't call gcry_pk_testkey because it is only implemented for
+     private keys.  */
+  /* err = gcry_pk_testkey (keyparam); */
+  /* if (err) */
+  /*   fail ("gcry_pk_testkey failed for sample public key: %s\n", */
+  /*         gpg_strerror (err)); */
+
+  gcry_ctx_release (ctx);
+  err = gcry_mpi_ec_new (&ctx, keyparam, NULL);
+  if (err)
+    fail ("gcry_mpi_ec_new failed for sample public key: %s\n",
+          gpg_strerror (err));
+  else
+    {
+      gcry_sexp_t sexp;
+
+      get_and_cmp_mpi ("q", sample_ed25519_q, "Ed25519", ctx);
+      get_and_cmp_point ("q", sample_ed25519_q_x, sample_ed25519_q_y,
+                         "Ed25519", ctx);
+      get_and_cmp_mpi ("q@eddsa", sample_ed25519_q_eddsa, "Ed25519", ctx);
+
+      /* Delete Q by setting d and the clearing d.  The clearing is
+         required so that we can check whether Q has been cleared and
+         because further tests only expect a public key. */
+      d = hex2mpi (sample_ed25519_d);
+      err = gcry_mpi_ec_set_mpi ("d", d, ctx);
+      if (err)
+        fail ("setting d for Ed25519 failed: %s\n", gpg_strerror (err));
+      gcry_mpi_release (d);
+      err = gcry_mpi_ec_set_mpi ("d", NULL, ctx);
+      if (err)
+        fail ("setting d for Ed25519 failed(2): %s\n", gpg_strerror (err));
+      if (gcry_mpi_ec_get_mpi ("q", ctx, 0))
+        fail ("setting d for Ed25519 did not reset Q\n");
+
+      /* Set Q again.  We need to use an opaque MPI here because
+         sample_ed25519_q is in uncompressed format which can only be
+         auto-detected if passed opaque.  */
+      q = hex2mpiopa (sample_ed25519_q);
+      err = gcry_mpi_ec_set_mpi ("q", q, ctx);
+      if (err)
+        fail ("setting Q for Ed25519 failed: %s\n", gpg_strerror (err));
+      gcry_mpi_release (q);
+      get_and_cmp_mpi ("q", sample_ed25519_q, "Ed25519(2)", ctx);
+
+      /* Get as s-expression.  */
       err = gcry_pubkey_get_sexp (&sexp, 0, ctx);
       if (err)
         fail ("gcry_pubkey_get_sexp(0) failed: %s\n", gpg_strerror (err));
@@ -544,9 +712,9 @@ context_param (void)
               gpg_strerror (err));
       gcry_sexp_release (sexp);
 
-      gcry_ctx_release (ctx);
     }
 
+  gcry_ctx_release (ctx);
   gcry_sexp_release (keyparam);
 }
 
@@ -730,7 +898,7 @@ basic_ec_math_simplified (void)
     print_sexp ("Result of gcry_pubkey_get_sexp (GET_PUBKEY):\n", sexp);
   gcry_sexp_release (sexp);
 
-  /* Does get_sexp return the public key if after d has been deleted?  */
+  /* Does get_sexp return the public key after d has been deleted?  */
   err = gcry_mpi_ec_set_mpi ("d", NULL, ctx);
   if (err)
     die ("gcry_mpi_ec_set_mpi(d=NULL) failed\n");