mpi: Add functions to manipulate an EC context.
authorWerner Koch <wk@gnupg.org>
Fri, 15 Mar 2013 13:43:19 +0000 (14:43 +0100)
committerWerner Koch <wk@gnupg.org>
Fri, 15 Mar 2013 13:53:42 +0000 (14:53 +0100)
* src/gcrypt.h.in (gcry_mpi_ec_p_new): Remove.
(gcry_mpi_ec_new): New.
(gcry_mpi_ec_get_mpi): New.
(gcry_mpi_ec_get_point): New.
(gcry_mpi_ec_set_mpi): New.
(gcry_mpi_ec_set_point): New.
* src/visibility.c (gcry_mpi_ec_p_new): Remove.
* mpi/ec.c (_gcry_mpi_ec_p_new): Make it an internal function and
change to return an error code.
(_gcry_mpi_ec_get_mpi): New.
(_gcry_mpi_ec_get_point): New.
(_gcry_mpi_ec_set_mpi): New.
(_gcry_mpi_ec_set_point): New.
* src/mpi.h: Add new prototypes.
* src/ec-context.h: New.
* mpi/ec.c: Include that header.
(mpi_ec_ctx_s): Move to ec-context.h, add new fields, and put some
fields into an inner struct.
(point_copy): New.
* cipher/ecc.c (fill_in_curve): Allow passing NULL for R_NBITS.
(mpi_from_keyparam, point_from_keyparam): New.
(_gcry_mpi_ec_new): New.

* tests/t-mpi-point.c (test-curve): New.
(ec_p_new): New.  Use it instead of the removed gcry_mpi_ec_p_new.
(get_and_cmp_mpi, get_and_cmp_point): New.
(context_param): New test.
(basic_ec_math_simplified): New test.
(main): Call new tests.

* src/context.c (_gcry_ctx_get_pointer): Check for a NULL CTX.
--

gcry_mpi_ec_p_new() was a specialized version of the more general new
gcry_mpi_ec_new().  It was added to master only a few days ago, thus
there should be no problem to remove it.  A replacement can easily be
written (cf. t-mpi-point.c).

Note that gcry_mpi_ec_set_mpi and gcry_mpi_ec_set_point have not yet
been tested.

14 files changed:
NEWS
cipher/ecc.c
doc/gcrypt.texi
mpi/ec.c
src/Makefile.am
src/context.c
src/ec-context.h [new file with mode: 0644]
src/gcrypt.h.in
src/libgcrypt.def
src/libgcrypt.vers
src/mpi.h
src/visibility.c
src/visibility.h
tests/t-mpi-point.c

diff --git a/NEWS b/NEWS
index c0a7e8e..429f666 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -46,7 +46,12 @@ Noteworthy changes in version 1.6.0 (unreleased)
  gcry_mpi_point_snatch_set       NEW.
  gcry_ctx_t                      NEW.
  gcry_ctx_release                NEW.
- gcry_mpi_ec_p_new               NEW.
+ gcry_mpi_ec_new                 NEW.
+ gcry_mpi_ec_get_mpi             NEW.
+ gcry_mpi_ec_get_point           NEW.
+ gcry_mpi_ec_set_mpi             NEW.
+ gcry_mpi_ec_set_point           NEW.
+ gcry_mpi_ec_get_affine          NEW.
  gcry_mpi_ec_dup                 NEW.
  gcry_mpi_ec_add                 NEW.
  gcry_mpi_ec_mul                 NEW.
index 4efbef4..c95a57a 100644 (file)
 
   - 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.
+
+  - Split this up into several files.  For example the curve
+    management and gcry_mpi_ec_new are independent of the actual ECDSA
+    implementation.  This will also help to support optimized versions
+    of some curves.
+
 */
 
 
 #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"
 
 /* Definition of a curve.  */
 typedef struct
@@ -440,8 +449,8 @@ gen_k (gcry_mpi_t p, int security_level)
 
 /* 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.  */
+   parameters of the named curve or those of a suitable curve.  If
+   R_NBITS is not NULL, the chosen number of bits is stored there.  */
 static gpg_err_code_t
 fill_in_curve (unsigned int nbits, const char *name,
                elliptic_curve_t *curve, unsigned int *r_nbits)
@@ -491,7 +500,8 @@ fill_in_curve (unsigned int nbits, const char *name,
   if (fips_mode () && !domain_parms[idx].fips )
     return GPG_ERR_NOT_SUPPORTED;
 
-  *r_nbits = domain_parms[idx].nbits;
+  if (r_nbits)
+    *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);
@@ -1689,6 +1699,277 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
 }
 
 
+\f
+/*
+   Low-level API helper functions.
+ */
+
+/* Helper to extract an MPI from key parameters.  */
+static gpg_err_code_t
+mpi_from_keyparam (gcry_mpi_t *r_a, gcry_sexp_t keyparam, const char *name)
+{
+  gcry_err_code_t ec = 0;
+  gcry_sexp_t l1;
+
+  l1 = gcry_sexp_find_token (keyparam, name, 0);
+  if (l1)
+    {
+      *r_a = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+      gcry_sexp_release (l1);
+      if (!*r_a)
+        ec = GPG_ERR_INV_OBJ;
+    }
+  return ec;
+}
+
+/* Helper to extract a point from key parameters.  If no parameter
+   with NAME is found, the functions tries to find a non-encoded point
+   by appending ".x", ".y" and ".z" to NAME.  ".z" is in this case
+   optional and defaults to 1.  */
+static gpg_err_code_t
+point_from_keyparam (gcry_mpi_point_t *r_a,
+                     gcry_sexp_t keyparam, const char *name)
+{
+  gcry_err_code_t ec;
+  gcry_mpi_t a = NULL;
+  gcry_mpi_point_t point;
+
+  ec = mpi_from_keyparam (&a, keyparam, name);
+  if (ec)
+    return ec;
+
+  if (a)
+    {
+      point = gcry_mpi_point_new (0);
+      ec = os2ec (point, a);
+      if (ec)
+        {
+          gcry_mpi_point_release (point);
+          mpi_free (a);
+          return ec;
+        }
+    }
+  else
+    {
+      char *tmpname;
+      gcry_mpi_t x = NULL;
+      gcry_mpi_t y = NULL;
+      gcry_mpi_t z = NULL;
+
+      tmpname = gcry_malloc (strlen (name) + 2 + 1);
+      if (!tmpname)
+        return gpg_err_code_from_syserror ();
+      strcpy (stpcpy (tmpname, name), ".x");
+      ec = mpi_from_keyparam (&x, keyparam, tmpname);
+      if (ec)
+        {
+          gcry_free (tmpname);
+          return ec;
+        }
+      strcpy (stpcpy (tmpname, name), ".y");
+      ec = mpi_from_keyparam (&y, keyparam, tmpname);
+      if (ec)
+        {
+          mpi_free (x);
+          gcry_free (tmpname);
+          return ec;
+        }
+      strcpy (stpcpy (tmpname, name), ".z");
+      ec = mpi_from_keyparam (&z, keyparam, tmpname);
+      if (ec)
+        {
+          mpi_free (y);
+          mpi_free (x);
+          gcry_free (tmpname);
+          return ec;
+        }
+      if (!z)
+        z = mpi_set_ui (NULL, 1);
+      if (x && y)
+        point = gcry_mpi_point_snatch_set (NULL, x, y, z);
+      else
+        {
+          mpi_free (x);
+          mpi_free (y);
+          mpi_free (z);
+          point = NULL;
+        }
+      gcry_free (tmpname);
+    }
+
+  if (point)
+    *r_a = point;
+  return 0;
+}
+
+
+/* This function creates a new context for elliptic curve operations.
+   Either KEYPARAM or CURVENAME must be given.  If both are given and
+   KEYPARAM has no curve parameter CURVENAME is used to add missing
+   parameters.  On success 0 is returned and the new context stored at
+   R_CTX.  On error NULL is stored at R_CTX and an error code is
+   returned.  The context needs to be released using
+   gcry_ctx_release.  */
+gpg_err_code_t
+_gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
+                  gcry_sexp_t keyparam, const char *curvename)
+{
+  gpg_err_code_t errc;
+  gcry_ctx_t ctx = NULL;
+  gcry_mpi_t p = NULL;
+  gcry_mpi_t a = NULL;
+  gcry_mpi_t b = NULL;
+  gcry_mpi_point_t G = NULL;
+  gcry_mpi_t n = NULL;
+  gcry_mpi_point_t Q = NULL;
+  gcry_mpi_t d = NULL;
+  gcry_sexp_t l1;
+
+  *r_ctx = NULL;
+
+  if (keyparam)
+    {
+      errc = mpi_from_keyparam (&p, keyparam, "p");
+      if (errc)
+        goto leave;
+      errc = mpi_from_keyparam (&a, keyparam, "a");
+      if (errc)
+        goto leave;
+      errc = mpi_from_keyparam (&b, keyparam, "b");
+      if (errc)
+        goto leave;
+      errc = point_from_keyparam (&G, keyparam, "G");
+      if (errc)
+        goto leave;
+      errc = mpi_from_keyparam (&n, keyparam, "n");
+      if (errc)
+        goto leave;
+      errc = point_from_keyparam (&Q, keyparam, "Q");
+      if (errc)
+        goto leave;
+      errc = mpi_from_keyparam (&d, keyparam, "d");
+      if (errc)
+        goto leave;
+    }
+
+
+  /* Check whether a curve parameter is available and use that to fill
+     in missing values.  If no curve parameter is available try an
+     optional provided curvename.  If only the curvename has been
+     given use that one. */
+  if (keyparam)
+    l1 = gcry_sexp_find_token (keyparam, "curve", 5);
+  else
+    l1 = NULL;
+  if (l1 || curvename)
+    {
+      char *name;
+      elliptic_curve_t *E;
+
+      if (l1)
+        {
+          name = _gcry_sexp_nth_string (l1, 1);
+          gcry_sexp_release (l1);
+          if (!name)
+            {
+              errc = GPG_ERR_INV_OBJ; /* Name missing or out of core. */
+              goto leave;
+            }
+        }
+      else
+        name = NULL;
+
+      E = gcry_calloc (1, sizeof *E);
+      if (!E)
+        {
+          errc = gpg_err_code_from_syserror ();
+          gcry_free (name);
+          goto leave;
+        }
+
+      errc = fill_in_curve (0, name? name : curvename, E, NULL);
+      gcry_free (name);
+      if (errc)
+        {
+          gcry_free (E);
+          goto leave;
+        }
+
+      if (!p)
+        {
+          p = E->p;
+          E->p = NULL;
+        }
+      if (!a)
+        {
+          a = E->a;
+          E->a = NULL;
+        }
+      if (!b)
+        {
+          b = E->b;
+          E->b = NULL;
+        }
+      if (!G)
+        {
+          G = gcry_mpi_point_snatch_set (NULL, E->G.x, E->G.y, E->G.z);
+          E->G.x = NULL;
+          E->G.y = NULL;
+          E->G.z = NULL;
+        }
+      if (!n)
+        {
+          n = E->n;
+          E->n = NULL;
+        }
+      curve_free (E);
+      gcry_free (E);
+    }
+
+  errc = _gcry_mpi_ec_p_new (&ctx, p, a);
+  if (!errc)
+    {
+      mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
+
+      if (b)
+        {
+          ec->b = b;
+          b = NULL;
+        }
+      if (G)
+        {
+          ec->G = G;
+          G = NULL;
+        }
+      if (n)
+        {
+          ec->n = n;
+          n = NULL;
+        }
+      if (Q)
+        {
+          ec->Q = Q;
+          Q = NULL;
+        }
+      if (d)
+        {
+          ec->d = d;
+          d = NULL;
+        }
+
+      *r_ctx = ctx;
+    }
+
+ leave:
+  mpi_free (p);
+  mpi_free (a);
+  mpi_free (b);
+  gcry_mpi_point_release (G);
+  mpi_free (n);
+  gcry_mpi_point_release (Q);
+  mpi_free (d);
+  return errc;
+}
 
 
 \f
index a56d527..4d48eb4 100644 (file)
@@ -2058,6 +2058,7 @@ and no @var{x-mpi}.
 @node ECC key parameters
 @subsection ECC key parameters
 
+@anchor{ecc_keyparam}
 @noindent
 An ECC private key is described by this S-expression:
 
@@ -2084,7 +2085,7 @@ Base point @math{g}.
 @item n-mpi
 Order of @math{g}
 @item q-point
-The point representing the public key @math{Q = dP}.
+The point representing the public key @math{Q = dG}.
 @item d-mpi
 The private key @math{d}
 @end table
@@ -3919,16 +3920,71 @@ some extra memory allocations and copies.  Returns @var{point} or the
 newly allocated point object.
 @end deftypefun
 
-@anchor{gcry_mpi_ec_p_new}
-@deftypefun gcry_ctx_t gcry_mpi_ec_p_new (@w{gcry_mpi_t @var{p}}, @
- @w{gcry_mpi_t @var{a}}
+@anchor{gcry_mpi_ec_new}
+@deftypefun gpg_error_t gcry_mpi_ec_p_new (@w{gpg_ctx_t *@var{r_ctx}}, @
+ @w{gcry_sexp_t @var{keyparam}}, @w{const char *@var{curvename}})
 
-Allocate a new context for elliptic curve operations based on the
-field GF(p).  @var{p} is the prime specifying this field, @var{a} is
-the first coefficient of the Weierstrass equation.  The function
-returns a context object which eventually needs to be released using
-@ref{gcry_ctx_release}.  On error this function returns @code{NULL}
-and sets @code{errno}.
+Allocate a new context for elliptic curve operations.  If
+@var{keyparam} is given it specifies the parameters of the curve
+(@pxref{ecc_keyparam}).  If @var{curvename} is given in addition to
+@var{keyparam} and the key parameters do not include a named curve
+reference, the string @var{curvename} is used to fill in missing
+parameters.  If only @var{curvename} is given, the context is
+initialized for this named curve.
+
+If a parameter specifying a point (e.g. @code{g} or @code{q}) is not
+found, the parser looks for a non-encoded point by appending
+@code{.x}, @code{.y}, and @code{.z} to the parameter name and looking
+them all up to create a point.  A parameter with the suffix @code{.z}
+is optional and defaults to 1.
+
+On success the function returns 0 and stores the new context object at
+@var{r_ctx}; this object eventually needs to be released
+(@pxref{gcry_ctx_release}).  On error the function stores @code{NULL} at
+@var{r_ctx} and returns an error code.
+@end deftypefun
+
+@deftypefun gcry_mpi_t gcry_mpi_ec_get_mpi ( @
+ @w{const char *@var{name}}, @w{gcry_ctx_t @var{ctx}}, @w{int @var{copy}})
+
+Return the MPI with @var{name} from the context @var{ctx}.  If not
+found @code{NULL} is returned.  If the returned MPI may later be
+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}.
+@end deftypefun
+
+@deftypefun gcry_mpi_point_t gcry_mpi_ec_get_point ( @
+ @w{const char *@var{name}}, @w{gcry_ctx_t @var{ctx}}, @w{int @var{copy}})
+
+Return the point with @var{name} from the context @var{ctx}.  If not
+found @code{NULL} is returned.  If the returned MPI may later be
+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 point.  In any case @code{gcry_mpi_point_release} needs to be
+called to release the result.
+@end deftypefun
+
+@deftypefun gpg_error_t gcry_mpi_ec_set_mpi ( @
+ @w{const char *@var{name}}, @w{gcry_mpi_t @var{newvalue}}, @
+ @w{gcry_ctx_t @var{ctx}})
+
+Store the MPI @var{newvalue} at @var{name} into the context @var{ctx}.
+On success @code{0} is returned; on error an error code.  Valid names
+are the MPI parameters of an elliptic curve (@pxref{ecc_keyparam}).
+@end deftypefun
+
+@deftypefun gpg_error_t gcry_mpi_ec_set_point ( @
+ @w{const char *@var{name}}, @w{gcry_mpi_point_t @var{newvalue}}, @
+ @w{gcry_ctx_t @var{ctx}})
+
+Store the point @var{newvalue} at @var{name} into the context
+@var{ctx}.  On success @code{0} is returned; on error an error code.
+Valid names are the point parameters of an elliptic curve
+(@pxref{ecc_keyparam}).
 @end deftypefun
 
 @deftypefun int gcry_mpi_ec_get_affine ( @
@@ -3938,9 +3994,9 @@ and sets @code{errno}.
 Compute the affine coordinates from the projective coordinates in
 @var{point} and store them into @var{x} and @var{y}.  If one
 coordinate is not required, @code{NULL} may be passed to @var{x} or
-@var{y}.  @var{ctx} is the context object which for example may have
-been created using @ref{gcry_mpi_ec_p_new}. Returns 0 on success or
-not 0 if @var{point} is at infinity.
+@var{y}.  @var{ctx} is the context object which has been created using
+@code{gcry_mpi_ec_new}. Returns 0 on success or not 0 if @var{point}
+is at infinity.
 @end deftypefun
 
 @deftypefun void gcry_mpi_ec_dup ( @
index 7f310ea..9a6868b 100644 (file)
--- a/mpi/ec.c
+++ b/mpi/ec.c
 #include "longlong.h"
 #include "g10lib.h"
 #include "context.h"
+#include "ec-context.h"
 
 
 #define point_init(a)  _gcry_mpi_point_init ((a))
 #define point_free(a)  _gcry_mpi_point_free_parts ((a))
 
 
-/* Object to represent a point in projective coordinates. */
-/* Currently defined in mpi.h */
-
-/* This context is used with all our EC functions. */
-struct mpi_ec_ctx_s
-{
-  /* Domain parameters.  */
-  gcry_mpi_t p;   /* Prime specifying the field GF(p).  */
-  gcry_mpi_t a;   /* First coefficient of the Weierstrass equation.  */
-
-  int a_is_pminus3;  /* True if A = P - 3. */
-
-  gcry_mpi_t two_inv_p;
-
-  /* Scratch variables.  */
-  gcry_mpi_t scratch[11];
-
-  /* Helper for fast reduction.  */
-/*   int nist_nbits; /\* If this is a NIST curve, the number of bits.  *\/ */
-/*   gcry_mpi_t s[10]; */
-/*   gcry_mpi_t c; */
-
-};
-
-
 /* Create a new point option.  NBITS gives the size in bits of one
    coordinate; it is only used to pre-allocate some resources and
    might also be passed as 0 to use a default value.  */
@@ -116,6 +92,24 @@ point_set (mpi_point_t d, mpi_point_t s)
   mpi_set (d->z, s->z);
 }
 
+
+/* Return a copy of POINT.  */
+static gcry_mpi_point_t
+point_copy (gcry_mpi_point_t point)
+{
+  gcry_mpi_point_t newpoint;
+
+  if (point)
+    {
+      newpoint = gcry_mpi_point_new (0);
+      point_set (newpoint, point);
+    }
+  else
+    newpoint = NULL;
+  return newpoint;
+}
+
+
 /* Set the projective coordinates from POINT into X, Y, and Z.  If a
    coordinate is not required, X, Y, or Z may be passed as NULL.  */
 void
@@ -353,27 +347,22 @@ ec_p_init (mpi_ec_t ctx, gcry_mpi_t p, gcry_mpi_t a)
   int i;
   gcry_mpi_t tmp;
 
-  mpi_normalize (p);
-  mpi_normalize (a);
-
-  /* Fixme: Do we want to check some constraints? e.g.
-     a < p
-  */
+  /* Fixme: Do we want to check some constraints? e.g.  a < p  */
 
   ctx->p = mpi_copy (p);
   ctx->a = mpi_copy (a);
 
   tmp = mpi_alloc_like (ctx->p);
   mpi_sub_ui (tmp, ctx->p, 3);
-  ctx->a_is_pminus3 = !mpi_cmp (ctx->a, tmp);
+  ctx->t.a_is_pminus3 = !mpi_cmp (ctx->a, tmp);
   mpi_free (tmp);
 
-  ctx->two_inv_p = mpi_alloc (0);
-  ec_invm (ctx->two_inv_p, mpi_const (MPI_C_TWO), ctx);
+  ctx->t.two_inv_p = mpi_alloc (0);
+  ec_invm (ctx->t.two_inv_p, mpi_const (MPI_C_TWO), ctx);
 
   /* Allocate scratch variables.  */
-  for (i=0; i< DIM(ctx->scratch); i++)
-    ctx->scratch[i] = mpi_alloc_like (ctx->p);
+  for (i=0; i< DIM(ctx->t.scratch); i++)
+    ctx->t.scratch[i] = mpi_alloc_like (ctx->p);
 
   /* Prepare for fast reduction.  */
   /* FIXME: need a test for NIST values.  However it does not gain us
@@ -401,13 +390,22 @@ ec_deinit (void *opaque)
   mpi_ec_t ctx = opaque;
   int i;
 
+  /* Domain parameter.  */
   mpi_free (ctx->p);
   mpi_free (ctx->a);
+  mpi_free (ctx->b);
+  gcry_mpi_point_release (ctx->G);
+  mpi_free (ctx->n);
+
+  /* The key.  */
+  gcry_mpi_point_release (ctx->Q);
+  mpi_free (ctx->d);
 
-  mpi_free (ctx->two_inv_p);
+  /* Private data of ec.c.  */
+  mpi_free (ctx->t.two_inv_p);
 
-  for (i=0; i< DIM(ctx->scratch); i++)
-    mpi_free (ctx->scratch[i]);
+  for (i=0; i< DIM(ctx->t.scratch); i++)
+    mpi_free (ctx->t.scratch[i]);
 
 /*   if (ctx->nist_nbits == 192) */
 /*     { */
@@ -455,28 +453,128 @@ _gcry_mpi_ec_free (mpi_ec_t ctx)
 
 /* This function returns a new context for elliptic curve operations
    based on the field GF(p).  P is the prime specifying this field, A
-   is the first coefficient.  This function is part of the public API.
-   On error this function returns NULL and sets ERRNO.
-   The context needs to be released using gcry_ctx_release.  */
-gcry_ctx_t
-gcry_mpi_ec_p_new (gcry_mpi_t p, gcry_mpi_t a)
+   is the first coefficient.  On success the new context is stored at
+   R_CTX and 0 is returned; on error NULL is stored at R_CTX and an
+   error code is returned.  The context needs to be released using
+   gcry_ctx_release.  This is an internal fucntions.  */
+gpg_err_code_t
+_gcry_mpi_ec_p_new (gcry_ctx_t *r_ctx, gcry_mpi_t p, gcry_mpi_t a)
 {
   gcry_ctx_t ctx;
   mpi_ec_t ec;
 
+  *r_ctx = NULL;
   if (!p || !a || !mpi_cmp_ui (a, 0))
-    {
-      gpg_err_set_errno (EINVAL);
-      return NULL;
-    }
+    return GPG_ERR_EINVAL;
 
   ctx = _gcry_ctx_alloc (CONTEXT_TYPE_EC, sizeof *ec, ec_deinit);
   if (!ctx)
-    return NULL;
+    return gpg_err_code_from_syserror ();
   ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
   ec_p_init (ec, p, a);
 
-  return ctx;
+  *r_ctx = ctx;
+  return 0;
+}
+
+gcry_mpi_t
+_gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy)
+{
+  mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
+
+  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)
+    return mpi_is_const (ec->a) && !copy? ec->a : mpi_copy (ec->a);
+  if (!strcmp (name, "b") && ec->b)
+    return mpi_is_const (ec->b) && !copy? ec->b : mpi_copy (ec->b);
+  if (!strcmp (name, "n") && ec->n)
+    return mpi_is_const (ec->n) && !copy? ec->n : mpi_copy (ec->n);
+  if (!strcmp (name, "d") && ec->d)
+    return mpi_is_const (ec->d) && !copy? ec->d : mpi_copy (ec->d);
+  if (!strcmp (name, "g.x") && ec->G && ec->G->x)
+    return mpi_is_const (ec->G->x) && !copy? ec->G->x : mpi_copy (ec->G->x);
+  if (!strcmp (name, "g.y") && ec->G && ec->G->y)
+    return mpi_is_const (ec->G->y) && !copy? ec->G->y : mpi_copy (ec->G->y);
+
+  return NULL;
+}
+
+
+gcry_mpi_point_t
+_gcry_mpi_ec_get_point (const char *name, gcry_ctx_t ctx, int copy)
+{
+  mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
+
+  (void)copy;  /* Not used.  */
+
+  if (!strcmp (name, "g") && ec->G)
+    return point_copy (ec->G);
+  if (!strcmp (name, "q") && ec->Q)
+    return point_copy (ec->Q);
+
+  return NULL;
+}
+
+
+gpg_err_code_t
+_gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue,
+                      gcry_ctx_t ctx)
+{
+  mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
+
+  if (!strcmp (name, "p"))
+    {
+      mpi_free (ec->p);
+      ec->p = mpi_copy (newvalue);
+    }
+  else if (!strcmp (name, "a"))
+    {
+      mpi_free (ec->a);
+      ec->a = mpi_copy (newvalue);
+    }
+  else if (!strcmp (name, "b"))
+    {
+      mpi_free (ec->b);
+      ec->b = mpi_copy (newvalue);
+    }
+  else if (!strcmp (name, "n"))
+    {
+      mpi_free (ec->n);
+      ec->n = mpi_copy (newvalue);
+    }
+  else if (!strcmp (name, "d"))
+    {
+      mpi_free (ec->d);
+      ec->d = mpi_copy (newvalue);
+    }
+  else
+    return GPG_ERR_UNKNOWN_NAME;
+
+  return 0;
+}
+
+
+gpg_err_code_t
+_gcry_mpi_ec_set_point (const char *name, gcry_mpi_point_t newvalue,
+                        gcry_ctx_t ctx)
+{
+  mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
+
+  if (!strcmp (name, "g"))
+    {
+      gcry_mpi_point_release (ec->G);
+      ec->G = point_copy (newvalue);
+    }
+  else if (!strcmp (name, "q"))
+    {
+      gcry_mpi_point_release (ec->Q);
+      ec->Q = point_copy (newvalue);
+    }
+  else
+    return GPG_ERR_UNKNOWN_NAME;
+
+  return 0;
 }
 
 
@@ -523,12 +621,12 @@ _gcry_mpi_ec_dup_point (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
 #define x3 (result->x)
 #define y3 (result->y)
 #define z3 (result->z)
-#define t1 (ctx->scratch[0])
-#define t2 (ctx->scratch[1])
-#define t3 (ctx->scratch[2])
-#define l1 (ctx->scratch[3])
-#define l2 (ctx->scratch[4])
-#define l3 (ctx->scratch[5])
+#define t1 (ctx->t.scratch[0])
+#define t2 (ctx->t.scratch[1])
+#define t3 (ctx->t.scratch[2])
+#define l1 (ctx->t.scratch[3])
+#define l2 (ctx->t.scratch[4])
+#define l3 (ctx->t.scratch[5])
 
   if (!mpi_cmp_ui (point->y, 0) || !mpi_cmp_ui (point->z, 0))
     {
@@ -539,7 +637,7 @@ _gcry_mpi_ec_dup_point (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
     }
   else
     {
-      if (ctx->a_is_pminus3)  /* Use the faster case.  */
+      if (ctx->t.a_is_pminus3)  /* Use the faster case.  */
         {
           /* L1 = 3(X - Z^2)(X + Z^2) */
           /*                          T1: used for Z^2. */
@@ -615,17 +713,17 @@ _gcry_mpi_ec_add_points (mpi_point_t result,
 #define x3 (result->x)
 #define y3 (result->y)
 #define z3 (result->z)
-#define l1 (ctx->scratch[0])
-#define l2 (ctx->scratch[1])
-#define l3 (ctx->scratch[2])
-#define l4 (ctx->scratch[3])
-#define l5 (ctx->scratch[4])
-#define l6 (ctx->scratch[5])
-#define l7 (ctx->scratch[6])
-#define l8 (ctx->scratch[7])
-#define l9 (ctx->scratch[8])
-#define t1 (ctx->scratch[9])
-#define t2 (ctx->scratch[10])
+#define l1 (ctx->t.scratch[0])
+#define l2 (ctx->t.scratch[1])
+#define l3 (ctx->t.scratch[2])
+#define l4 (ctx->t.scratch[3])
+#define l5 (ctx->t.scratch[4])
+#define l6 (ctx->t.scratch[5])
+#define l7 (ctx->t.scratch[6])
+#define l8 (ctx->t.scratch[7])
+#define l9 (ctx->t.scratch[8])
+#define t1 (ctx->t.scratch[9])
+#define t2 (ctx->t.scratch[10])
 
   if ( (!mpi_cmp (x1, x2)) && (!mpi_cmp (y1, y2)) && (!mpi_cmp (z1, z2)) )
     {
@@ -715,7 +813,7 @@ _gcry_mpi_ec_add_points (mpi_point_t result,
           ec_powm (t1, l3, mpi_const (MPI_C_THREE), ctx); /* fixme: Use saved value*/
           ec_mulm (t1, t1, l8, ctx);
           ec_subm (y3, l9, t1, ctx);
-          ec_mulm (y3, y3, ctx->two_inv_p, ctx);
+          ec_mulm (y3, y3, ctx->t.two_inv_p, ctx);
         }
     }
 
index 1869ad3..713e616 100644 (file)
@@ -60,6 +60,7 @@ libgcrypt_la_SOURCES = g10lib.h visibility.c visibility.h types.h \
        stdmem.c stdmem.h secmem.c secmem.h \
        mpi.h missing-string.c module.c fips.c \
        hmac256.c hmac256.h context.c context.h \
+       ec-context.h \
        ath.h ath.c
 
 EXTRA_libgcrypt_la_SOURCES = hwf-x86.c
index ae991c5..2c02c9c 100644 (file)
@@ -86,7 +86,7 @@ _gcry_ctx_alloc (int type, size_t length, void (*deinit)(void*))
 void *
 _gcry_ctx_get_pointer (gcry_ctx_t ctx, int type)
 {
-  if (memcmp (ctx->magic, CTX_MAGIC, CTX_MAGIC_LEN))
+  if (!ctx || memcmp (ctx->magic, CTX_MAGIC, CTX_MAGIC_LEN))
     log_fatal ("bad pointer %p passed to _gcry_ctx_get_pointer\n", ctx);
   if (ctx->type != type)
     log_fatal ("wrong context type %d request for context %p of type %d\n",
diff --git a/src/ec-context.h b/src/ec-context.h
new file mode 100644 (file)
index 0000000..88742bf
--- /dev/null
@@ -0,0 +1,57 @@
+/* ec-context.h - Private definitions for CONTEXT_TYPE_EC.
+ * 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/>.
+ */
+
+#ifndef GCRY_EC_CONTEXT_H
+#define GCRY_EC_CONTEXT_H
+
+/* This context is used with all our EC functions. */
+struct mpi_ec_ctx_s
+{
+  /* 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).  */
+  gcry_mpi_t a;         /* First coefficient of the Weierstrass equation.  */
+  gcry_mpi_t b;         /* Second coefficient of the Weierstrass equation.  */
+  gcry_mpi_point_t G;   /* Base point (generator).  */
+  gcry_mpi_t n;         /* Order of G.  */
+
+  /* The actual key.  May not be set.  */
+  gcry_mpi_point_t Q;   /* Public key.   */
+  gcry_mpi_t d;         /* Private key.  */
+
+
+  /* This structure is private to mpi/ec.c! */
+  struct {
+    int a_is_pminus3;  /* True if A = P - 3. */
+
+    gcry_mpi_t two_inv_p;
+
+    /* Scratch variables.  */
+    gcry_mpi_t scratch[11];
+
+    /* Helper for fast reduction.  */
+    /*   int nist_nbits; /\* If this is a NIST curve, the # of bits.  *\/ */
+    /*   gcry_mpi_t s[10]; */
+    /*   gcry_mpi_t c; */
+  } t;
+};
+
+
+
+#endif /*GCRY_EC_CONTEXT_H*/
index eb9a11d..ad4da04 100644 (file)
@@ -606,9 +606,24 @@ gcry_mpi_point_t gcry_mpi_point_snatch_set (gcry_mpi_point_t point,
                                             gcry_mpi_t z);
 
 /* Allocate a new context for elliptic curve operations based on the
-   field GF(p).  P is the prime specifying this field, A is the first
-   coefficient.  Returns NULL on error.  */
-gcry_ctx_t gcry_mpi_ec_p_new (gcry_mpi_t p, gcry_mpi_t a);
+   parameters given by KEYPARAM or using CURVENAME.  */
+gpg_error_t gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
+                             gcry_sexp_t keyparam, const char *curvename);
+
+/* Get a named MPI from an elliptic curve context.  */
+gcry_mpi_t gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy);
+
+/* Get a named point from an elliptic curve context.  */
+gcry_mpi_point_t gcry_mpi_ec_get_point (const char *name,
+                                        gcry_ctx_t ctx, int copy);
+
+/* Store a named MPI into an elliptic curve context.  */
+gpg_error_t gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue,
+                                 gcry_ctx_t ctx);
+
+/* Store a named point into an elliptic curve context.  */
+gpg_error_t gcry_mpi_ec_set_point (const char *name, gcry_mpi_point_t newvalue,
+                                   gcry_ctx_t ctx);
 
 /* Store the affine coordinates of POINT into X and Y.  */
 int gcry_mpi_ec_get_affine (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_point_t point,
index 611f10f..061c7e3 100644 (file)
@@ -223,8 +223,12 @@ EXPORTS
 
       gcry_ctx_release          @202
 
-      gcry_mpi_ec_p_new         @203
-      gcry_mpi_ec_get_affine    @204
-      gcry_mpi_ec_dup           @205
-      gcry_mpi_ec_add           @206
-      gcry_mpi_ec_mul           @207
+      gcry_mpi_ec_new           @203
+      gcry_mpi_ec_get_mpi       @204
+      gcry_mpi_ec_get_point     @205
+      gcry_mpi_ec_set_mpi       @206
+      gcry_mpi_ec_set_point     @207
+      gcry_mpi_ec_get_affine    @208
+      gcry_mpi_ec_dup           @209
+      gcry_mpi_ec_add           @210
+      gcry_mpi_ec_mul           @211
index 4a375b2..65959d3 100644 (file)
@@ -90,7 +90,9 @@ GCRYPT_1.6 {
     gcry_mpi_point_new; gcry_mpi_point_release;
     gcry_mpi_point_get; gcry_mpi_point_snatch_get;
     gcry_mpi_point_set; gcry_mpi_point_snatch_set;
-    gcry_mpi_ec_p_new;
+    gcry_mpi_ec_new;
+    gcry_mpi_ec_get_mpi; gcry_mpi_ec_get_point;
+    gcry_mpi_ec_set_mpi; gcry_mpi_ec_set_point;
     gcry_mpi_ec_get_affine;
     gcry_mpi_ec_dup; gcry_mpi_ec_add; gcry_mpi_ec_mul;
 
index 9c22141..b727d5f 100644 (file)
--- a/src/mpi.h
+++ b/src/mpi.h
@@ -289,6 +289,19 @@ void _gcry_mpi_ec_mul_point (mpi_point_t result,
                              gcry_mpi_t scalar, mpi_point_t point,
                              mpi_ec_t ctx);
 
+gpg_err_code_t _gcry_mpi_ec_p_new (gcry_ctx_t *r_ctx,
+                                   gcry_mpi_t p, gcry_mpi_t a);
+gpg_err_code_t _gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
+                                 gcry_sexp_t keyparam, const char *curvename);
+gcry_mpi_t _gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy);
+gcry_mpi_point_t _gcry_mpi_ec_get_point (const char *name,
+                                         gcry_ctx_t ctx, int copy);
+gpg_err_code_t _gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue,
+                                     gcry_ctx_t ctx);
+gpg_err_code_t _gcry_mpi_ec_set_point (const char *name,
+                                       gcry_mpi_point_t newvalue,
+                                       gcry_ctx_t ctx);
+
 
 
 #endif /*G10_MPI_H*/
index 5c3216d..ed68b86 100644 (file)
@@ -461,10 +461,36 @@ gcry_mpi_point_snatch_set (gcry_mpi_point_t point,
   return _gcry_mpi_point_snatch_set (point, x, y, z);
 }
 
-gcry_ctx_t
-gcry_mpi_ec_p_new (gcry_mpi_t p, gcry_mpi_t a)
+gpg_error_t
+gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
+                 gcry_sexp_t keyparam, const char *curvename)
+{
+  return gpg_error (_gcry_mpi_ec_new (r_ctx, keyparam, curvename));
+}
+
+gcry_mpi_t
+gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy)
+{
+  return _gcry_mpi_ec_get_mpi (name, ctx, copy);
+}
+
+gcry_mpi_point_t
+gcry_mpi_ec_get_point (const char *name, gcry_ctx_t ctx, int copy)
+{
+  return _gcry_mpi_ec_get_point (name, ctx, copy);
+}
+
+gpg_error_t
+gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue, gcry_ctx_t ctx)
+{
+  return gpg_error (_gcry_mpi_ec_set_mpi (name, newvalue, ctx));
+}
+
+gpg_error_t
+gcry_mpi_ec_set_point (const char *name, gcry_mpi_point_t newvalue,
+                        gcry_ctx_t ctx)
 {
-  return _gcry_mpi_ec_p_new (p, a);
+  return gpg_error (_gcry_mpi_ec_set_point (name, newvalue, ctx));
 }
 
 int
index 90c6ad1..031537a 100644 (file)
 #define gcry_mpi_copy               _gcry_mpi_copy
 #define gcry_mpi_div                _gcry_mpi_div
 #define gcry_mpi_dump               _gcry_mpi_dump
-#define gcry_mpi_ec_p_new           _gcry_mpi_ec_p_new
 #define gcry_mpi_gcd                _gcry_mpi_gcd
 #define gcry_mpi_get_flag           _gcry_mpi_get_flag
 #define gcry_mpi_get_nbits          _gcry_mpi_get_nbits
@@ -370,7 +369,6 @@ gcry_err_code_t gcry_md_get (gcry_md_hd_t hd, int algo,
 #undef gcry_mpi_copy
 #undef gcry_mpi_div
 #undef gcry_mpi_dump
-#undef gcry_mpi_ec_p_new
 #undef gcry_mpi_gcd
 #undef gcry_mpi_get_flag
 #undef gcry_mpi_get_nbits
@@ -550,7 +548,11 @@ MARK_VISIBLEX(gcry_mpi_ec_add)
 MARK_VISIBLEX(gcry_mpi_ec_dup)
 MARK_VISIBLEX(gcry_mpi_ec_get_affine)
 MARK_VISIBLEX(gcry_mpi_ec_mul)
-MARK_VISIBLE (gcry_mpi_ec_p_new)
+MARK_VISIBLEX(gcry_mpi_ec_new)
+MARK_VISIBLEX(gcry_mpi_ec_get_mpi)
+MARK_VISIBLEX(gcry_mpi_ec_get_point)
+MARK_VISIBLEX(gcry_mpi_ec_set_mpi)
+MARK_VISIBLEX(gcry_mpi_ec_set_point)
 MARK_VISIBLE (gcry_mpi_gcd)
 MARK_VISIBLE (gcry_mpi_get_flag)
 MARK_VISIBLE (gcry_mpi_get_nbits)
index 8714d38..31df12b 100644 (file)
@@ -41,6 +41,84 @@ static int error_count;
 #define xfree(a)      gcry_free ((a))
 #define pass() do { ; } while (0)
 
+
+static struct
+{
+  const char *desc;           /* Description of the 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.  */
+} test_curve[] =
+  {
+    {
+      "NIST P-192",
+      "0xfffffffffffffffffffffffffffffffeffffffffffffffff",
+      "0xfffffffffffffffffffffffffffffffefffffffffffffffc",
+      "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
+      "0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
+
+      "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
+      "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811"
+    },
+    {
+      "NIST P-224",
+      "0xffffffffffffffffffffffffffffffff000000000000000000000001",
+      "0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
+      "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
+      "0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d" ,
+
+      "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
+      "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
+    },
+    {
+      "NIST P-256",
+      "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
+      "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
+      "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
+      "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
+
+      "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
+      "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
+    },
+    {
+      "NIST P-384",
+      "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+      "ffffffff0000000000000000ffffffff",
+      "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+      "ffffffff0000000000000000fffffffc",
+      "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a"
+      "c656398d8a2ed19d2a85c8edd3ec2aef",
+      "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf"
+      "581a0db248b0a77aecec196accc52973",
+
+      "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38"
+      "5502f25dbf55296c3a545e3872760ab7",
+      "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0"
+      "0a60b1ce1d7e819d7a431d7c90ea0e5f"
+    },
+    {
+      "NIST P-521",
+      "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+      "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
+      "0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10"
+      "9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
+      "0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      "ffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
+
+      "0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d"
+      "baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
+      "0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6"
+      "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650"
+    },
+    { NULL, NULL, NULL, NULL, NULL }
+  };
+
+
+
+
 static void
 show (const char *format, ...)
 {
@@ -131,6 +209,30 @@ cmp_mpihex (gcry_mpi_t a, const char *b)
 }
 
 
+/* Wrapper to emulate the libgcrypt internal EC context allocation
+   function.  */
+static gpg_error_t
+ec_p_new (gcry_ctx_t *r_ctx, gcry_mpi_t p, gcry_mpi_t a)
+{
+  gpg_error_t err;
+  gcry_sexp_t sexp;
+
+  if (p && a)
+    err = gcry_sexp_build (&sexp, NULL, "(ecdsa (p %m)(a %m))", p, a);
+  else if (p)
+    err = gcry_sexp_build (&sexp, NULL, "(ecdsa (p %m))", p);
+  else if (a)
+    err = gcry_sexp_build (&sexp, NULL, "(ecdsa (a %m))", a);
+  else
+    err = gcry_sexp_build (&sexp, NULL, "(ecdsa)");
+  if (err)
+    return err;
+  err = gcry_mpi_ec_new (r_ctx, sexp, NULL);
+  gcry_sexp_release (sexp);
+  return err;
+}
+
+
 \f
 static void
 set_get_point (void)
@@ -189,6 +291,7 @@ set_get_point (void)
 static void
 context_alloc (void)
 {
+  gpg_error_t err;
   gcry_ctx_t ctx;
   gcry_mpi_t p, a;
 
@@ -197,40 +300,153 @@ context_alloc (void)
 
   p = gcry_mpi_set_ui (NULL, 1);
   a = gcry_mpi_set_ui (NULL, 1);
-  ctx = gcry_mpi_ec_p_new (p, a);
-  if (!ctx)
-    die ("gcry_mpi_ec_p_new returned an error: %s\n",
-         gpg_strerror (gpg_error_from_syserror ()));
+  err = ec_p_new (&ctx, p, a);
+  if (err)
+    die ("ec_p_new returned an error: %s\n", gpg_strerror (err));
   gcry_mpi_release (p);
   gcry_mpi_release (a);
   gcry_ctx_release (ctx);
 
   p = gcry_mpi_set_ui (NULL, 0);
   a = gcry_mpi_set_ui (NULL, 0);
-  ctx = gcry_mpi_ec_p_new (p, a);
-  if (ctx || gpg_err_code_from_syserror () != GPG_ERR_EINVAL)
-    fail ("gcry_mpi_ec_p_new: bad parameter detection failed (1)\n");
+  err = ec_p_new (&ctx, p, a);
+  if (!err || gpg_err_code (err) != GPG_ERR_EINVAL)
+    fail ("ec_p_new: bad parameter detection failed (1)\n");
 
   gcry_mpi_set_ui (p, 1);
-  ctx = gcry_mpi_ec_p_new (p, a);
-  if (ctx || gpg_err_code_from_syserror () != GPG_ERR_EINVAL)
-    fail ("gcry_mpi_ec_p_new: bad parameter detection failed (2)\n");
+  err = ec_p_new (&ctx, p, a);
+  if (!err || gpg_err_code (err) != GPG_ERR_EINVAL)
+    fail ("ec_p_new: bad parameter detection failed (2)\n");
 
   gcry_mpi_release (p);
   p = NULL;
-  ctx = gcry_mpi_ec_p_new (p, a);
-  if (ctx || gpg_err_code_from_syserror () != GPG_ERR_EINVAL)
-    fail ("gcry_mpi_ec_p_new: bad parameter detection failed (3)\n");
+  err = ec_p_new (&ctx, p, a);
+  if (!err || gpg_err_code (err) != GPG_ERR_EINVAL)
+    fail ("ec_p_new: bad parameter detection failed (3)\n");
 
   gcry_mpi_release (a);
   a = NULL;
-  ctx = gcry_mpi_ec_p_new (p, a);
-  if (ctx || gpg_err_code_from_syserror () != GPG_ERR_EINVAL)
-    fail ("gcry_mpi_ec_p_new: bad parameter detection failed (4)\n");
+  err = ec_p_new (&ctx, p, a);
+  if (!err || gpg_err_code (err) != GPG_ERR_EINVAL)
+    fail ("ec_p_new: bad parameter detection failed (4)\n");
+
+}
+
+
+static int
+get_and_cmp_mpi (const char *name, const char *mpistring, const char *desc,
+                 gcry_ctx_t ctx)
+{
+  gcry_mpi_t mpi;
+
+  mpi = gcry_mpi_ec_get_mpi (name, ctx, 1);
+  if (!mpi)
+    {
+      fail ("error getting parameter '%s' of curve '%s'\n", name, desc);
+      return 1;
+    }
+  if (cmp_mpihex (mpi, mpistring))
+    {
+      fail ("parameter '%s' of curve '%s' does not match\n", name, desc);
+      gcry_mpi_release (mpi);
+      return 1;
+    }
+  gcry_mpi_release (mpi);
+  return 0;
+}
+
+
+static int
+get_and_cmp_point (const char *name,
+                   const char *mpi_x_string, const char *mpi_y_string,
+                   const char *desc, gcry_ctx_t ctx)
+{
+  gcry_mpi_point_t point;
+  gcry_mpi_t x, y, z;
+  int result = 0;
+
+  point = gcry_mpi_ec_get_point (name, ctx, 1);
+  if (!point)
+    {
+      fail ("error getting point parameter '%s' of curve '%s'\n", name, desc);
+      return 1;
+    }
+
+  x = gcry_mpi_new (0);
+  y = gcry_mpi_new (0);
+  z = gcry_mpi_new (0);
+  gcry_mpi_point_snatch_get (x, y, z, point);
+  if (cmp_mpihex (x, mpi_x_string))
+    {
+      fail ("x coordinate of '%s' of curve '%s' does not match\n", name, desc);
+      result = 1;
+    }
+  if (cmp_mpihex (y, mpi_y_string))
+    {
+      fail ("y coordinate of '%s' of curve '%s' does not match\n", name, desc);
+      result = 1;
+    }
+  if (cmp_mpihex (z, "01"))
+    {
+      fail ("z coordinate of '%s' of curve '%s' is not 1\n", name, desc);
+      result = 1;
+    }
+  gcry_mpi_release (x);
+  gcry_mpi_release (y);
+  gcry_mpi_release (z);
+  return result;
+}
+
+
+static void
+context_param (void)
+{
+  gpg_error_t err;
+  int idx;
+  gcry_ctx_t ctx = NULL;
+
+  wherestr = "context_param";
+
+  for (idx=0; test_curve[idx].desc; idx++)
+    {
+      show ("checking curve '%s'\n", test_curve[idx].desc);
+      gcry_ctx_release (ctx);
+      err = gcry_mpi_ec_new (&ctx, NULL, test_curve[idx].desc);
+      if (err)
+        {
+          fail ("can't create context for curve '%s': %s\n",
+                test_curve[idx].desc, gpg_strerror (err));
+          continue;
+        }
+      if (get_and_cmp_mpi ("p", test_curve[idx].p, test_curve[idx].desc, ctx))
+        continue;
+      if (get_and_cmp_mpi ("a", test_curve[idx].a, test_curve[idx].desc, ctx))
+        continue;
+      if (get_and_cmp_mpi ("b", test_curve[idx].b, test_curve[idx].desc, ctx))
+        continue;
+      if (get_and_cmp_mpi ("g.x",test_curve[idx].g_x, test_curve[idx].desc,ctx))
+        continue;
+      if (get_and_cmp_mpi ("g.y",test_curve[idx].g_y, test_curve[idx].desc,ctx))
+        continue;
+      if (get_and_cmp_mpi ("n", test_curve[idx].n, test_curve[idx].desc, ctx))
+        continue;
+      if (get_and_cmp_point ("g", test_curve[idx].g_x, test_curve[idx].g_y,
+                             test_curve[idx].desc, ctx))
+        continue;
+
+    }
+  gcry_ctx_release (ctx);
+
+  /* FIXME: Add tests for Q and d.  */
+
+  /* FIXME: Add test sor the set functions.  */
+
 
 }
 
 
+
+
 /* Create a new point from (X,Y,Z) given as hex strings.  */
 gcry_mpi_point_t
 make_point (const char *x, const char *y, const char *z)
@@ -244,9 +460,13 @@ make_point (const char *x, const char *y, const char *z)
 }
 
 
+/* This tests checks that the low-level EC API yields the same result
+   as using the high level API.  The values have been taken from a
+   test run using the high level API.  */
 static void
 basic_ec_math (void)
 {
+  gpg_error_t err;
   gcry_ctx_t ctx;
   gcry_mpi_t P, A;
   gcry_mpi_point_t G, Q;
@@ -264,7 +484,9 @@ basic_ec_math (void)
   d = hex2mpi ("D4EF27E32F8AD8E2A1C6DDEBB1D235A69E3CEF9BCE90273D");
   Q = gcry_mpi_point_new (0);
 
-  ctx = gcry_mpi_ec_p_new (P, A);
+  err = ec_p_new (&ctx, P, A);
+  if (err)
+    die ("ec_p_new failed: %s\n", gpg_strerror (err));
   gcry_mpi_ec_mul (Q, d, G, ctx);
 
   x = gcry_mpi_new (0);
@@ -304,6 +526,66 @@ basic_ec_math (void)
 }
 
 
+/* This is the same as basic_ec_math but uses more advanced
+   features.  */
+static void
+basic_ec_math_simplified (void)
+{
+  gpg_error_t err;
+  gcry_ctx_t ctx;
+  gcry_mpi_point_t G, Q;
+  gcry_mpi_t d;
+  gcry_mpi_t x, y, z;
+
+  wherestr = "set_get_point";
+  show ("checking basic math functions for EC (variant)\n");
+
+  d = hex2mpi ("D4EF27E32F8AD8E2A1C6DDEBB1D235A69E3CEF9BCE90273D");
+  Q = gcry_mpi_point_new (0);
+
+  err = gcry_mpi_ec_new (&ctx, NULL, "NIST P-192");
+  if (err)
+    die ("gcry_mpi_ec_new failed: %s\n", gpg_strerror (err));
+  G = gcry_mpi_ec_get_point ("g", ctx, 1);
+  if (!G)
+    die ("gcry_mpi_ec_get_point(G) failed\n");
+  gcry_mpi_ec_mul (Q, d, G, ctx);
+
+  x = gcry_mpi_new (0);
+  y = gcry_mpi_new (0);
+  z = gcry_mpi_new (0);
+  gcry_mpi_point_get (x, y, z, Q);
+  if (cmp_mpihex (x, "222D9EC717C89D047E0898C9185B033CD11C0A981EE6DC66")
+      || cmp_mpihex (y, "605DE0A82D70D3E0F84A127D0739ED33D657DF0D054BFDE8")
+      || cmp_mpihex (z, "00B06B519071BC536999AC8F2D3934B3C1FC9EACCD0A31F88F"))
+    fail ("computed public key does not match\n");
+  if (debug)
+    {
+      print_mpi ("Q.x", x);
+      print_mpi ("Q.y", y);
+      print_mpi ("Q.z", z);
+    }
+
+  if (gcry_mpi_ec_get_affine (x, y, Q, ctx))
+    fail ("failed to get affine coordinates\n");
+  if (cmp_mpihex (x, "008532093BA023F4D55C0424FA3AF9367E05F309DC34CDC3FE")
+      || cmp_mpihex (y, "00C13CA9E617C6C8487BFF6A726E3C4F277913D97117939966"))
+    fail ("computed affine coordinates of public key do not match\n");
+  if (debug)
+    {
+      print_mpi ("q.x", x);
+      print_mpi ("q.y", y);
+    }
+
+  gcry_mpi_release (z);
+  gcry_mpi_release (y);
+  gcry_mpi_release (x);
+  gcry_mpi_point_release (Q);
+  gcry_mpi_release (d);
+  gcry_mpi_point_release (G);
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -324,7 +606,9 @@ main (int argc, char **argv)
 
   set_get_point ();
   context_alloc ();
+  context_param ();
   basic_ec_math ();
+  basic_ec_math_simplified ();
 
   show ("All tests completed. Errors: %d\n", error_count);
   return error_count ? 1 : 0;