Add gcry_pubkey_get_sexp.
authorWerner Koch <wk@gnupg.org>
Thu, 11 Apr 2013 18:27:46 +0000 (20:27 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 11 Apr 2013 18:27:46 +0000 (20:27 +0200)
* src/gcrypt.h.in (GCRY_PK_GET_PUBKEY): New.
(GCRY_PK_GET_SECKEY): New.
(gcry_pubkey_get_sexp): New.
* src/visibility.c (gcry_pubkey_get_sexp): New.
* src/visibility.h (gcry_pubkey_get_sexp): Mark visible.
* src/libgcrypt.def, src/libgcrypt.vers: Add new function.
* cipher/pubkey-internal.h: New.
* cipher/Makefile.am (libcipher_la_SOURCES): Add new file.
* cipher/ecc.c: Include pubkey-internal.h
(_gcry_pk_ecc_get_sexp): New.
* cipher/pubkey.c: Include pubkey-internal.h and context.h.
(_gcry_pubkey_get_sexp): New.
* src/context.c (_gcry_ctx_find_pointer): New.
* src/cipher-proto.h: Add _gcry_pubkey_get_sexp.
* tests/t-mpi-point.c (print_sexp): New.
(context_param, basic_ec_math_simplified): Add tests for the new
function.

* configure.ac (NEED_GPG_ERROR_VERSION): Set to 1.11.
(AH_BOTTOM) Add error codes from gpg-error 1.12
* src/g10lib.h (fips_not_operational): Use GPG_ERR_NOT_OPERATIONAL.

* mpi/ec.c (_gcry_mpi_ec_get_mpi): Fix computation of Q.
(_gcry_mpi_ec_get_point): Ditto.
--

While checking the new code I figured that the auto-computation of Q
must have led to a segv.  It seems we had no test case for that.

Signed-off-by: Werner Koch <wk@gnupg.org>
18 files changed:
NEWS
cipher/Makefile.am
cipher/ecc.c
cipher/pubkey-internal.h [new file with mode: 0644]
cipher/pubkey.c
configure.ac
doc/gcrypt.texi
mpi/ec.c
src/cipher-proto.h
src/context.c
src/context.h
src/g10lib.h
src/gcrypt.h.in
src/libgcrypt.def
src/libgcrypt.vers
src/visibility.c
src/visibility.h
tests/t-mpi-point.c

diff --git a/NEWS b/NEWS
index 926e531..c4f89c1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,8 @@ Noteworthy changes in version 1.6.0 (unreleased)
 ------------------------------------------------
 
  * Removed the long deprecated gcry_ac interface.  Thus Libgcrypt is
-   not anymore ABI compatible too previous versions.
+   not anymore ABI compatible to previous versions if they used the ac
+   interface.
 
  * Removed the module register subsystem.
 
@@ -61,6 +62,7 @@ Noteworthy changes in version 1.6.0 (unreleased)
  GCRYMPI_FLAG_CONST              NEW.
  GCRYPT_VERSION_NUMBER           NEW.
  GCRY_KDF_SCRYPT                 NEW.
+ gcry_pubkey_get_sexp            NEW.
 
 
 Noteworthy changes in version 1.5.0 (2011-06-29)
index 5189794..396e5a2 100644 (file)
@@ -40,7 +40,8 @@ libcipher_la_LIBADD = $(GCRYPT_MODULES)
 libcipher_la_SOURCES = \
 cipher.c cipher-internal.h \
 cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
-pubkey.c md.c \
+pubkey.c pubkey-internal.h \
+md.c \
 kdf.c kdf-internal.h \
 hmac-tests.c \
 bithelp.h  \
index fbd8c6a..34ed2c3 100644 (file)
@@ -66,6 +66,7 @@
 #include "cipher.h"
 #include "context.h"
 #include "ec-context.h"
+#include "pubkey-internal.h"
 
 /* Definition of a curve.  */
 typedef struct
@@ -1985,6 +1986,79 @@ _gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
 }
 
 
+/* This is the wroker function for gcry_pubkey_get_sexp for ECC
+   algorithms.  Note that the caller has already stored NULL at
+   R_SEXP.  */
+gpg_err_code_t
+_gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
+{
+  gpg_err_code_t rc;
+  gcry_mpi_t mpi_G = NULL;
+  gcry_mpi_t mpi_Q = NULL;
+
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n)
+    return GPG_ERR_BAD_CRYPT_CTX;
+
+  if (mode == GCRY_PK_GET_SECKEY && !ec->d)
+    return GPG_ERR_NO_SECKEY;
+
+  /* Compute the public point if it is missing.  */
+  if (!ec->Q && ec->d)
+    {
+      ec->Q = gcry_mpi_point_new (0);
+      _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
+    }
+
+  /* Encode G and Q.  */
+  mpi_G = _gcry_mpi_ec_ec2os (ec->G, ec);
+  if (!mpi_G)
+    {
+      rc = GPG_ERR_BROKEN_PUBKEY;
+      goto leave;
+    }
+  if (!ec->Q)
+    {
+      rc = GPG_ERR_BAD_CRYPT_CTX;
+      goto leave;
+    }
+  mpi_Q = _gcry_mpi_ec_ec2os (ec->Q, ec);
+  if (!mpi_Q)
+    {
+      rc = GPG_ERR_BROKEN_PUBKEY;
+      goto leave;
+    }
+
+  /* Fixme: We should return a curve name instead of the parameters if
+     if know that they match a curve.  */
+
+  if (ec->d && (!mode || mode == GCRY_PK_GET_SECKEY))
+    {
+      /* Let's return a private key. */
+      rc = gpg_err_code
+        (gcry_sexp_build
+         (r_sexp, NULL,
+          "(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)(d%m)))",
+          ec->p, ec->a, ec->b, mpi_G, ec->n, mpi_Q, ec->d));
+    }
+  else if (ec->Q)
+    {
+      /* Let's return a public key.  */
+      rc = gpg_err_code
+        (gcry_sexp_build
+         (r_sexp, NULL,
+          "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)))",
+          ec->p, ec->a, ec->b, mpi_G, ec->n, mpi_Q));
+    }
+  else
+    rc = GPG_ERR_BAD_CRYPT_CTX;
+
+ leave:
+  mpi_free (mpi_Q);
+  mpi_free (mpi_G);
+  return rc;
+}
+
+
 \f
 /*
      Self-test section.
diff --git a/cipher/pubkey-internal.h b/cipher/pubkey-internal.h
new file mode 100644 (file)
index 0000000..0ca17a5
--- /dev/null
@@ -0,0 +1,29 @@
+/* pubkey-internal.h  - Internal defs for pubkey.c
+ * 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_PUBKEY_INTERNAL_H
+#define GCRY_PUBKEY_INTERNAL_H
+
+
+/*-- ecc.c --*/
+gpg_err_code_t _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode,
+                                      mpi_ec_t ec);
+
+
+#endif /*GCRY_PUBKEY_INTERNAL_H*/
index 8b6356f..00317b5 100644 (file)
@@ -1,6 +1,7 @@
 /* pubkey.c  - pubkey dispatcher
  * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005,
  *               2007, 2008, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 g10 Code GmbH
  *
  * This file is part of Libgcrypt.
  *
@@ -28,6 +29,8 @@
 #include "mpi.h"
 #include "cipher.h"
 #include "ath.h"
+#include "context.h"
+#include "pubkey-internal.h"
 
 
 static gcry_err_code_t pubkey_decrypt (int algo, gcry_mpi_t *result,
@@ -4068,6 +4071,48 @@ gcry_pk_algo_info (int algorithm, int what, void *buffer, size_t *nbytes)
 }
 
 
+/* Return an S-expression representing the context CTX.  Depending on
+   the state of that context, the S-expression may either be a public
+   key, a private key or any other object used with public key
+   operations.  On success a new S-expression is stored at R_SEXP and
+   0 is returned, on error NULL is store there and an error code is
+   returned.  MODE is either 0 or one of the GCRY_PK_GET_xxx values.
+
+   As of now it only support certain ECC operations because a context
+   object is right now only defined for ECC.  Over time this function
+   will be extended to cover more algorithms.  Note also that the name
+   of the function is gcry_pubkey_xxx and not gcry_pk_xxx.  The idea
+   is that we will eventually provide variants of the existing
+   gcry_pk_xxx functions which will take a context parameter.   */
+gcry_err_code_t
+_gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp, int mode, gcry_ctx_t ctx)
+{
+  mpi_ec_t ec;
+
+  if (!r_sexp)
+    return GPG_ERR_INV_VALUE;
+  *r_sexp = NULL;
+  switch (mode)
+    {
+    case 0:
+    case GCRY_PK_GET_PUBKEY:
+    case GCRY_PK_GET_SECKEY:
+      break;
+    default:
+      return GPG_ERR_INV_VALUE;
+    }
+  if (!ctx)
+    return GPG_ERR_NO_CRYPT_CTX;
+
+  ec = _gcry_ctx_find_pointer (ctx, CONTEXT_TYPE_EC);
+  if (ec)
+    return _gcry_pk_ecc_get_sexp (r_sexp, mode, ec);
+
+  return GPG_ERR_WRONG_CRYPT_CTX;
+}
+
+
+\f
 /* Explicitly initialize this module.  */
 gcry_err_code_t
 _gcry_pk_init (void)
index c597389..079951d 100644 (file)
@@ -1,7 +1,7 @@
 # Configure.ac script for Libgcrypt
 # Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006,
 #               2007, 2008, 2009, 2011 Free Software Foundation, Inc.
-# Copyright (C) 2012  g10 Code GmbH
+# Copyright (C) 2012, 2013  g10 Code GmbH
 #
 # This file is part of Libgcrypt.
 #
@@ -65,7 +65,7 @@ LIBGCRYPT_LT_REVISION=0
 # If the API is changed in an incompatible way: increment the next counter.
 LIBGCRYPT_CONFIG_API_VERSION=1
 
-NEED_GPG_ERROR_VERSION=1.8
+NEED_GPG_ERROR_VERSION=1.11
 
 PACKAGE=$PACKAGE_NAME
 VERSION=$PACKAGE_VERSION
@@ -105,10 +105,14 @@ AH_BOTTOM([
    properly prefixed.  */
 #define CAMELLIA_EXT_SYM_PREFIX _gcry_
 
-/* This error code is only available with gpg-error 1.7.  Thus
-   we define it here with the usual gcry prefix.  */
-#define GCRY_GPG_ERR_NOT_OPERATIONAL  176
-
+/* These error codes are used but not defined in the required
+   libgpg-error 1.11.  Define them here. */
+#define GPG_ERR_NO_CRYPT_CTX      191
+#define GPG_ERR_WRONG_CRYPT_CTX    192
+#define GPG_ERR_BAD_CRYPT_CTX     193
+#define GPG_ERR_CRYPT_CTX_CONFLICT 194
+#define GPG_ERR_BROKEN_PUBKEY      195
+#define GPG_ERR_BROKEN_SECKEY      196
 
 #endif /*_GCRYPT_CONFIG_H_INCLUDED*/
 ])
index 888a740..5d9bd2f 100644 (file)
@@ -2722,6 +2722,50 @@ algorithm provides them.
 @end deftypefun
 @c end gcry_pk_genkey
 
+
+@noindent
+Future versions of Libgcrypt will have extended versions of the public
+key interfaced which will take an additional context to allow for
+pre-computations, special operations, and other optimization.  As a
+first step a new function is introduced to help using the ECC
+algorithms in new ways:
+
+@deftypefun gcry_error_t gcry_pubkey_get_sexp (@w{gcry_sexp_t *@var{r_sexp}}, @
+ w{int @var{mode}}, @w{gcry_ctx_t @var{ctx}})
+
+Return an S-expression representing the context @var{ctx}.  Depending
+on the state of that context, the S-expression may either be a public
+key, a private key or any other object used with public key
+operations.  On success 0 is returned and a new S-expression is stored
+at @var{r_sexp}; on error an error code is returned and NULL is stored
+at @var{r_sexp}.  @var{mode} must be one of:
+
+@table @code
+@item 0
+Decide what to return depending on the context.  For example if the
+private key parameter is available a private key is returned, if not a
+public key is returned.
+
+@item GCRY_PK_GET_PUBKEY
+Return the public key even if the context has the private key
+parameter.
+
+@item GCRY_PK_GET_SECKEY
+Return the private key or the error @code{GPG_ERR_NO_SECKEY} if it is
+not possible.
+@end table
+
+As of now this function supports only certain ECC operations because a
+context object is right now only defined for ECC.  Over time this
+function will be extended to cover more algorithms.
+
+@end deftypefun
+@c end gcry_pubkey_get_sexp
+
+
+
+
+
 @c **********************************************************
 @c *******************  Hash Functions  *********************
 @c **********************************************************
index cd19c81..c736706 100644 (file)
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -546,7 +546,10 @@ _gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy)
     {
       /* If only the private key is given, compute the public key.  */
       if (!ec->Q && ec->d && ec->G && ec->p && ec->a)
-        _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
+        {
+          ec->Q = gcry_mpi_point_new (0);
+          _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
+        }
 
       if (ec->Q)
         return _gcry_mpi_ec_ec2os (ec->Q, ec);
@@ -569,7 +572,10 @@ _gcry_mpi_ec_get_point (const char *name, gcry_ctx_t ctx, int copy)
     {
       /* If only the private key is given, compute the public key.  */
       if (!ec->Q && ec->d && ec->G && ec->p && ec->a)
-        _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
+        {
+          ec->Q = gcry_mpi_point_new (0);
+          _gcry_mpi_ec_mul_point (ec->Q, ec->d, ec->G, ec);
+        }
 
       if (ec->Q)
         return point_copy (ec->Q);
index 347681f..e2f913d 100644 (file)
@@ -121,4 +121,10 @@ gcry_error_t _gcry_hmac_selftest (int algo, int extended,
 
 gcry_error_t _gcry_random_selftest (selftest_report_func_t report);
 
+
+/*-- pubkey.c --*/
+gcry_err_code_t _gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp,
+                                       int reserved, gcry_ctx_t ctx);
+
+
 #endif /*G10_CIPHER_PROTO_H*/
index 2c02c9c..1b8090d 100644 (file)
@@ -94,6 +94,25 @@ _gcry_ctx_get_pointer (gcry_ctx_t ctx, int type)
   return &ctx->u;
 }
 
+/* Return a pointer to the private part of the context CTX.  TYPE is
+   the requested context type.  Using an explicit type allows to cross
+   check the type and eventually allows to store several private
+   contexts in one context object.  In contrast to
+   _gcry_ctx_get_pointer, this function returns NULL if no context for
+   the given type was found.  If CTX is NULL the function does not
+   abort but returns NULL.  */
+void *
+_gcry_ctx_find_pointer (gcry_ctx_t ctx, int type)
+{
+  if (!ctx)
+    return NULL;
+  if (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)
+    return NULL;
+  return &ctx->u;
+}
+
 
 /* Release the generic context CTX.  */
 void
index 72f14d4..875de24 100644 (file)
@@ -26,6 +26,7 @@
 
 gcry_ctx_t _gcry_ctx_alloc (int type, size_t length, void (*deinit)(void*));
 void *_gcry_ctx_get_pointer (gcry_ctx_t ctx, int type);
+void *_gcry_ctx_find_pointer (gcry_ctx_t ctx, int type);
 
 
 #endif /*GCRY_CONTEXT_H*/
index d1bcfa9..23ea096 100644 (file)
@@ -394,7 +394,7 @@ void _gcry_fips_signal_error (const char *srcfile,
 
 int _gcry_fips_is_operational (void);
 #define fips_is_operational()   (_gcry_global_is_operational ())
-#define fips_not_operational()  (GCRY_GPG_ERR_NOT_OPERATIONAL)
+#define fips_not_operational()  (GPG_ERR_NOT_OPERATIONAL)
 
 int _gcry_fips_test_operational (void);
 int _gcry_fips_test_error_or_operational (void);
index 72fb6d3..85213ea 100644 (file)
@@ -944,6 +944,10 @@ enum gcry_pk_algos
 #define GCRY_PK_USAGE_AUTH 8   /* Good for authentication. */
 #define GCRY_PK_USAGE_UNKN 128 /* Unknown usage flag. */
 
+/* Modes used with gcry_pubkey_get_sexp.  */
+#define GCRY_PK_GET_PUBKEY 1
+#define GCRY_PK_GET_SECKEY 2
+
 /* Encrypt the DATA using the public key PKEY and store the result as
    a newly created S-expression at RESULT. */
 gcry_error_t gcry_pk_encrypt (gcry_sexp_t *result,
@@ -1007,6 +1011,9 @@ gcry_sexp_t gcry_pk_get_param (int algo, const char *name);
 #define gcry_pk_test_algo(a) \
             gcry_pk_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
 
+/* Return an S-expression representing the context CTX.  */
+gcry_error_t gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp,
+                                   int mode, gcry_ctx_t ctx);
 
 \f
 
index 061c7e3..4da4623 100644 (file)
@@ -232,3 +232,5 @@ EXPORTS
       gcry_mpi_ec_dup           @209
       gcry_mpi_ec_add           @210
       gcry_mpi_ec_mul           @211
+
+      gcry_pubkey_get_sexp      @212
index 65959d3..29e46db 100644 (file)
@@ -58,6 +58,8 @@ GCRYPT_1.6 {
     gcry_pk_testkey; gcry_pk_verify;
     gcry_pk_get_curve; gcry_pk_get_param;
 
+    gcry_pubkey_get_sexp;
+
     gcry_kdf_derive;
 
     gcry_prime_check; gcry_prime_generate;
index ed68b86..b503be6 100644 (file)
@@ -862,6 +862,17 @@ gcry_pk_get_param (int algo, const char *name)
 }
 
 gcry_error_t
+gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp, int mode, gcry_ctx_t ctx)
+{
+  if (!fips_is_operational ())
+    {
+      *r_sexp = NULL;
+      return gpg_error (fips_not_operational ());
+    }
+  return gpg_error (_gcry_pubkey_get_sexp (r_sexp, mode, ctx));
+}
+
+gcry_error_t
 gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
 {
   if (!fips_is_operational ())
index 031537a..1564e86 100644 (file)
@@ -494,6 +494,7 @@ MARK_VISIBLE (gcry_pk_map_name)
 MARK_VISIBLE (gcry_pk_sign)
 MARK_VISIBLE (gcry_pk_testkey)
 MARK_VISIBLE (gcry_pk_verify)
+MARK_VISIBLEX(gcry_pubkey_get_sexp)
 
 MARK_VISIBLE (gcry_kdf_derive)
 
index a3b6c56..9f7360e 100644 (file)
@@ -216,6 +216,23 @@ print_point (const char *text, gcry_mpi_point_t a)
 }
 
 
+static void
+print_sexp (const char *prefix, gcry_sexp_t a)
+{
+  char *buf;
+  size_t size;
+
+  if (prefix)
+    fputs (prefix, stderr);
+  size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+  buf = gcry_xmalloc (size);
+
+  gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
+  fprintf (stderr, "%.*s", (int)size, buf);
+  gcry_free (buf);
+}
+
+
 static gcry_mpi_t
 hex2mpi (const char *string)
 {
@@ -500,9 +517,34 @@ context_param (void)
           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",
                          ctx);
+
+      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);
+
+      gcry_ctx_release (ctx);
     }
 
   gcry_sexp_release (keyparam);
@@ -537,7 +579,7 @@ basic_ec_math (void)
   gcry_mpi_t d;
   gcry_mpi_t x, y, z;
 
-  wherestr = "set_get_point";
+  wherestr = "basic_ec_math";
   show ("checking basic math functions for EC\n");
 
   P = hex2mpi ("0xfffffffffffffffffffffffffffffffeffffffffffffffff");
@@ -600,8 +642,9 @@ basic_ec_math_simplified (void)
   gcry_mpi_point_t G, Q;
   gcry_mpi_t d;
   gcry_mpi_t x, y, z;
+  gcry_sexp_t sexp;
 
-  wherestr = "set_get_point";
+  wherestr = "basic_ec_math_simplified";
   show ("checking basic math functions for EC (variant)\n");
 
   d = hex2mpi ("D4EF27E32F8AD8E2A1C6DDEBB1D235A69E3CEF9BCE90273D");
@@ -644,6 +687,63 @@ basic_ec_math_simplified (void)
   gcry_mpi_release (z);
   gcry_mpi_release (y);
   gcry_mpi_release (x);
+
+  /* Let us also check wheer we can update the context.  */
+  err = gcry_mpi_ec_set_point ("g", G, ctx);
+  if (err)
+    die ("gcry_mpi_ec_set_point(G) failed\n");
+  err = gcry_mpi_ec_set_mpi ("d", d, ctx);
+  if (err)
+    die ("gcry_mpi_ec_set_mpi(d) failed\n");
+
+  /* FIXME: Below we need to check that the returned S-expression is
+     as requested.  For now we use manual inspection using --debug.  */
+
+  /* Does get_sexp return the private key?  */
+  err = gcry_pubkey_get_sexp (&sexp, 0, ctx);
+  if (err)
+    fail ("gcry_pubkey_get_sexp(0) failed: %s\n", gpg_strerror (err));
+  else if (verbose)
+    print_sexp ("Result of gcry_pubkey_get_sexp (0):\n", sexp);
+  gcry_sexp_release (sexp);
+
+  /* Does get_sexp return the public key if requested?  */
+  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 (verbose)
+    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?  */
+  err = gcry_mpi_ec_set_mpi ("d", NULL, ctx);
+  if (err)
+    die ("gcry_mpi_ec_set_mpi(d=NULL) failed\n");
+  err = gcry_pubkey_get_sexp (&sexp, 0, ctx);
+  if (err)
+    fail ("gcry_pubkey_get_sexp(0 w/o d) failed: %s\n", gpg_strerror (err));
+  else if (verbose)
+    print_sexp ("Result of gcry_pubkey_get_sexp (0 w/o d):\n", sexp);
+  gcry_sexp_release (sexp);
+
+  /* Does get_sexp return an error after d has been deleted?  */
+  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);
+
+  /* Does get_sexp return an error after d and Q have been deleted?  */
+  err = gcry_mpi_ec_set_point ("q", NULL, ctx);
+  if (err)
+    die ("gcry_mpi_ec_set_point(q=NULL) failed\n");
+  err = gcry_pubkey_get_sexp (&sexp, 0, ctx);
+  if (gpg_err_code (err) != GPG_ERR_BAD_CRYPT_CTX)
+    fail ("gcry_pubkey_get_sexp(0 w/o Q,d) returned wrong error: %s\n",
+          gpg_strerror (err));
+  gcry_sexp_release (sexp);
+
+
   gcry_mpi_point_release (Q);
   gcry_mpi_release (d);
   gcry_mpi_point_release (G);