sexp: Add function gcry_sexp_extract_param.
authorWerner Koch <wk@gnupg.org>
Wed, 16 Oct 2013 14:20:56 +0000 (16:20 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 16 Oct 2013 14:20:56 +0000 (16:20 +0200)
* src/gcrypt.h.in (_GCRY_GCC_ATTR_SENTINEL): New.
(gcry_sexp_extract_param): New.
* src/visibility.c (gcry_sexp_extract_param): New.
* src/visibility.h (gcry_sexp_extract_param): Add hack to detect
internal use.
* cipher/pubkey-util.c (_gcry_pk_util_extract_mpis): Move and split
into ...
* src/sexp.c (_gcry_sexp_vextract_param)
(_gcry_sexp_extract_param): this.  Change all callers.  Add support for buffer
descriptors and a path option/

* tests/tsexp.c (die, hex2buffer, hex2mpi, hex2mpiopa): New.
(cmp_mpihex, cmp_bufhex): New.
(check_extract_param): New.

Signed-off-by: Werner Koch <wk@gnupg.org>
18 files changed:
NEWS
cipher/dsa.c
cipher/ecc-curves.c
cipher/ecc-misc.c
cipher/ecc.c
cipher/elgamal.c
cipher/pubkey-internal.h
cipher/pubkey-util.c
cipher/rsa.c
doc/gcrypt.texi
src/g10lib.h
src/gcrypt.h.in
src/libgcrypt.def
src/libgcrypt.vers
src/sexp.c
src/visibility.c
src/visibility.h
tests/tsexp.c

diff --git a/NEWS b/NEWS
index ab326eb..d60e067 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -35,13 +35,13 @@ Noteworthy changes in version 1.6.0 (unreleased)
 
  * Added a scatter gather hash convenience function.
 
- * Added several MPI helper functions.
+ * Added several MPI amd SEXP helper functions.
 
  * Added support for negative numbers to gcry_mpi_print,
    gcry_mpi_aprint and gcry_mpi_scan.
 
  * The algorithm ids GCRY_PK_ECDSA and GCRY_PK_ECDH are now
-   deprecated.  Use GCRY_PK_ECC instead.
+   deprecated.  Use GCRY_PK_ECC if you need an algorithm id.
 
  * Interface changes relative to the 1.5.0 release:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -108,6 +108,7 @@ Noteworthy changes in version 1.6.0 (unreleased)
  GCRYCTL_DISABLE_PRIV_DROP       NEW.
  GCRY_CIPHER_SALSA20             NEW.
  gcry_sexp_nth_buffer            NEW.
+ gcry_sexp_extract_param         NEW.
  GCRY_CIPHER_SALSA20R12          NEW.
  GCRY_CIPHER_GOST28147           NEW.
  GCRY_MD_GOSTR3411_94            NEW.
index e43bdf4..45ad97a 100644 (file)
@@ -956,9 +956,9 @@ dsa_check_secret_key (gcry_sexp_t keyparms)
   gcry_err_code_t rc;
   DSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL};
 
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pqgyx",
-                                   &sk.p, &sk.q, &sk.g, &sk.y, &sk.x,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx",
+                                 &sk.p, &sk.q, &sk.g, &sk.y, &sk.x,
+                                 NULL);
   if (rc)
     goto leave;
 
@@ -998,8 +998,8 @@ dsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     log_mpidump ("dsa_sign   data", data);
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pqgyx",
-                                   &sk.p, &sk.q, &sk.g, &sk.y, &sk.x, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx",
+                                 &sk.p, &sk.q, &sk.g, &sk.y, &sk.x, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1065,7 +1065,7 @@ dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
   rc = _gcry_pk_util_preparse_sigval (s_sig, dsa_names, &l1, NULL);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "rs", &sig_r, &sig_s, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "rs", &sig_r, &sig_s, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1075,8 +1075,8 @@ dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (s_keyparms, "pqgy",
-                                   &pk.p, &pk.q, &pk.g, &pk.y, NULL);
+  rc = _gcry_sexp_extract_param (s_keyparms, NULL, "pqgy",
+                                 &pk.p, &pk.q, &pk.g, &pk.y, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
index 53433a2..2cdb9b4 100644 (file)
@@ -436,9 +436,9 @@ _gcry_ecc_get_curve (gcry_sexp_t keyparms, int iterator, unsigned int *r_nbits)
   /*
    * Extract the curve parameters..
    */
-  if (_gcry_pk_util_extract_mpis (keyparms, "-pabgn",
-                                  &E.p, &E.a, &E.b, &mpi_g, &E.n,
-                                  NULL))
+  if (_gcry_sexp_extract_param (keyparms, NULL, "-pabgn",
+                                &E.p, &E.a, &E.b, &mpi_g, &E.n,
+                                NULL))
     goto leave;
   if (mpi_g)
     {
index d89971f..fa0bded 100644 (file)
@@ -55,6 +55,7 @@ _gcry_ecc_curve_copy (elliptic_curve_t E)
 
   R.model = E.model;
   R.dialect = E.dialect;
+  R.name = E.name;
   R.p = mpi_copy (E.p);
   R.a = mpi_copy (E.a);
   R.b = mpi_copy (E.b);
index 3b75fea..1323d00 100644 (file)
@@ -1435,9 +1435,9 @@ ecc_check_secret_key (gcry_sexp_t keyparms)
   /*
    * Extract the key.
    */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?/q?+d",
-                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
-                                   &mpi_q, &sk.d, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?/q?+d",
+                                 &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                 &mpi_q, &sk.d, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
@@ -1552,9 +1552,9 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   /*
    * Extract the key.
    */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?/q?+d",
-                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
-                                   &mpi_q, &sk.d, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?/q?+d",
+                                 &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                 &mpi_q, &sk.d, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
@@ -1686,9 +1686,9 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
   rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1,
-                                   (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
-                                   &sig_r, &sig_s, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL,
+                                 (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
+                                 &sig_r, &sig_s, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1706,9 +1706,9 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
   /*
    * Extract the key.
    */
-  rc = _gcry_pk_util_extract_mpis (s_keyparms, "-p?a?b?g?n?/q?",
-                                   &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
-                                   &mpi_q, NULL);
+  rc = _gcry_sexp_extract_param (s_keyparms, NULL, "-p?a?b?g?n?/q?",
+                                 &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
+                                 &mpi_q, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
@@ -1890,9 +1890,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   /*
    * Extract the key.
    */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?+q",
-                                   &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
-                                   &mpi_q, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?+q",
+                                 &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
+                                 &mpi_q, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
@@ -2044,7 +2044,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "e", &data_e, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "e", &data_e, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -2058,9 +2058,9 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   /*
    * Extract the key.
    */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "-p?a?b?g?n?+d",
-                                   &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
-                                   &sk.d, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?+d",
+                                 &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
+                                 &sk.d, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
index 691e122..432ba6f 100644 (file)
@@ -735,9 +735,9 @@ elg_check_secret_key (gcry_sexp_t keyparms)
   gcry_err_code_t rc;
   ELG_secret_key sk = {NULL, NULL, NULL, NULL};
 
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pgyx",
-                                   &sk.p, &sk.g, &sk.y, &sk.x,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pgyx",
+                                 &sk.p, &sk.g, &sk.y, &sk.x,
+                                 NULL);
   if (rc)
     goto leave;
 
@@ -781,7 +781,8 @@ elg_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pgy", &pk.p, &pk.g, &pk.y, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pgy",
+                                 &pk.p, &pk.g, &pk.y, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -831,7 +832,7 @@ elg_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_preparse_encval (s_data, elg_names, &l1, &ctx);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "ab", &data_a, &data_b, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "ab", &data_a, &data_b, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -846,9 +847,9 @@ elg_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pgyx",
-                                   &sk.p, &sk.g, &sk.y, &sk.x,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pgyx",
+                                 &sk.p, &sk.g, &sk.y, &sk.x,
+                                 NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -940,8 +941,8 @@ elg_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "pgyx",
-                                   &sk.p, &sk.g, &sk.y, &sk.x, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "pgyx",
+                                 &sk.p, &sk.g, &sk.y, &sk.x, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1008,7 +1009,7 @@ elg_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
   rc = _gcry_pk_util_preparse_sigval (s_sig, elg_names, &l1, NULL);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "rs", &sig_r, &sig_s, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "rs", &sig_r, &sig_s, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1018,8 +1019,8 @@ elg_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (s_keyparms, "pgy",
-                                   &pk.p, &pk.g, &pk.y, NULL);
+  rc = _gcry_sexp_extract_param (s_keyparms, NULL, "pgy",
+                                 &pk.p, &pk.g, &pk.y, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
index cb2721d..db1399d 100644 (file)
@@ -28,9 +28,6 @@ gpg_err_code_t _gcry_pk_util_get_nbits (gcry_sexp_t list,
                                         unsigned int *r_nbits);
 gpg_err_code_t _gcry_pk_util_get_rsa_use_e (gcry_sexp_t list,
                                             unsigned long *r_e);
-gpg_err_code_t _gcry_pk_util_extract_mpis (gcry_sexp_t sexp,
-                                           const char *list, ...)
-                                           GCC_ATTR_SENTINEL(0);
 gpg_err_code_t _gcry_pk_util_preparse_sigval (gcry_sexp_t s_sig,
                                               const char **algo_names,
                                               gcry_sexp_t *r_parms,
index caf715e..0b90054 100644 (file)
@@ -273,119 +273,6 @@ _gcry_pk_util_get_rsa_use_e (gcry_sexp_t list, unsigned long *r_e)
 }
 
 
-/* Extract MPIs from an s-expression using a list of one letter
- * parameters.  The names of these parameters are given by the string
- * LIST.  Some special characters may be given to control the
- * conversion:
- *
- *    + :: Switch to unsigned integer format (default).
- *    - :: Switch to standard signed format.
- *    / :: Switch to opaque format.
- *    ? :: The previous parameter is optional.
- *
- * For each parameter name a pointer to an MPI variable is expected
- * and finally a NULL is expected.  Example:
- *
- *   _gcry_pk_util_extract_mpis (key, "n/x+ed", &mpi_n, &mpi_x, &mpi_e, NULL)
- *
- * This stores the parameter "N" from KEY as an unsigned MPI into
- * MPI_N, the parameter "X" as an opaque MPI into MPI_X, and the
- * parameter "E" again as an unsigned MPI into MPI_E.
- *
- * The function returns NULL on success.  On error an error code is
- * returned and the passed MPIs are either unchanged or set to NULL.
- */
-gpg_err_code_t
-_gcry_pk_util_extract_mpis (gcry_sexp_t sexp, const char *list, ...)
-{
-  va_list arg_ptr;
-  const char *s;
-  gcry_mpi_t *array[10];
-  int idx;
-  gcry_sexp_t l1;
-  enum gcry_mpi_format mpifmt = GCRYMPI_FMT_USG;
-
-  /* First copy all the args into an array.  This is required so that
-     we are able to release already allocated MPIs if later an error
-     was found.  */
-  va_start (arg_ptr, list) ;
-  for (s=list, idx=0; *s && idx < DIM (array); s++)
-    {
-      if (*s == '+' || *s == '-' || *s == '/' || *s == '?')
-        ;
-      else
-        {
-          array[idx] = va_arg (arg_ptr, gcry_mpi_t *);
-          if (!array[idx])
-            {
-              va_end (arg_ptr);
-              return GPG_ERR_INTERNAL; /* NULL pointer given.  */
-            }
-          idx++;
-        }
-    }
-  if (*s)
-    {
-      va_end (arg_ptr);
-      return GPG_ERR_INTERNAL;  /* Too many list elements.  */
-    }
-  if (va_arg (arg_ptr, gcry_mpi_t *))
-    {
-      va_end (arg_ptr);
-      return GPG_ERR_INTERNAL;  /* Not enough list elemends.  */
-    }
-  va_end (arg_ptr);
-
-  /* Now extract all parameters.  */
-  for (s=list, idx=0; *s; s++)
-    {
-      if (*s == '+')
-        mpifmt = GCRYMPI_FMT_USG;
-      else if (*s == '-')
-        mpifmt = GCRYMPI_FMT_STD;
-      else if (*s == '/')
-        mpifmt = GCRYMPI_FMT_HEX; /* Used to indicate opaque.  */
-      else if (*s == '?')
-        ; /* Only used via lookahead.  */
-      else
-        {
-          l1 = gcry_sexp_find_token (sexp, s, 1);
-          if (!l1 && s[1] == '?')
-            *array[idx] = NULL;       /* Optional element not found.  */
-          else if (!l1)
-            {
-              while (idx--)
-                {
-                  gcry_mpi_release (*array[idx]);
-                  *array[idx] = NULL;
-                }
-              return GPG_ERR_NO_OBJ;  /* List element not found.  */
-            }
-          else
-            {
-              if (mpifmt == GCRYMPI_FMT_HEX)
-                *array[idx] = _gcry_sexp_nth_opaque_mpi (l1, 1);
-              else
-                *array[idx] = gcry_sexp_nth_mpi (l1, 1, mpifmt);
-              gcry_sexp_release (l1);
-              if (!*array[idx])
-                {
-                  while (idx--)
-                    {
-                      gcry_mpi_release (*array[idx]);
-                      *array[idx] = NULL;
-                    }
-                  return GPG_ERR_INV_OBJ;  /* Conversion failed.  */
-                }
-            }
-          idx++;
-        }
-    }
-
-  return 0;
-}
-
-
 /* Parse a "sig-val" s-expression and store the inner parameter list at
    R_PARMS.  ALGO_NAMES is used to verify that the algorithm in
    "sig-val" is valid.  Returns 0 on success and stores a new list at
index d4d2a0a..fed52a1 100644 (file)
@@ -855,9 +855,9 @@ rsa_check_secret_key (gcry_sexp_t keyparms)
   RSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL, NULL};
 
   /* To check the key we need the optional parameters. */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "nedpqu",
-                                   &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "nedpqu",
+                                 &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
+                                 NULL);
   if (rc)
     goto leave;
 
@@ -902,7 +902,7 @@ rsa_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "ne", &pk.n, &pk.e, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "ne", &pk.n, &pk.e, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -969,7 +969,7 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_preparse_encval (s_data, rsa_names, &l1, &ctx);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "a", &data, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "a", &data, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -981,9 +981,9 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "nedp?q?u?",
-                                   &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "nedp?q?u?",
+                                 &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
+                                 NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1125,9 +1125,9 @@ rsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     }
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "nedp?q?u?",
-                                   &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
-                                   NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "nedp?q?u?",
+                                 &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u,
+                                 NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -1213,14 +1213,14 @@ rsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_preparse_sigval (s_sig, rsa_names, &l1, NULL);
   if (rc)
     goto leave;
-  rc = _gcry_pk_util_extract_mpis (l1, "s", &sig, NULL);
+  rc = _gcry_sexp_extract_param (l1, NULL, "s", &sig, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
     log_printmpi ("rsa_verify  sig", sig);
 
   /* Extract the key.  */
-  rc = _gcry_pk_util_extract_mpis (keyparms, "ne", &pk.n, &pk.e, NULL);
+  rc = _gcry_sexp_extract_param (keyparms, NULL, "ne", &pk.n, &pk.e, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
index 79d4d74..473c484 100644 (file)
@@ -3748,6 +3748,66 @@ this function to parse results of a public key function, you most
 likely want to use @code{GCRYMPI_FMT_USG}.
 @end deftypefun
 
+@deftypefun gpg_error_t gcry_sexp_extract_param ( @
+  @w{gcry_sexp_t @var{sexp}}, @
+  @w{const char *@var{path}}, @
+  @w{const char *@var{list}}, ...)
+
+Extract parameters from an S-expression using a list of single letter
+parameter names.  The names of these parameters are specified in
+LIST.  Some special characters may be given to control the
+conversion:
+
+@table @samp
+@item +
+Switch to unsigned integer format (GCRYMPI_FMT_USG).  This is the
+default mode.
+@item -
+Switch to standard signed format (GCRYMPI_FMT_STD).
+@item /
+Switch to opaque MPI format.  The resulting MPIs may not be used for
+computations; see @code{gcry_mpi_get_opaque} for details.
+@item &
+Switch to buffer descriptor mode.  See below for details.
+@item ?
+If immediately following a parameter letter, that parameter is
+considered optional.
+@end table
+
+Unless in buffer descriptor mode for each parameter name a pointer to
+an @code{gcry_mpi_t} variable is expected finally followed by a @code{NULL}.
+For example
+@example
+  _gcry_sexp_extract_param (key, NULL, "n/x+ed",
+                            &mpi_n, &mpi_x, &mpi_e, NULL)
+@end example
+
+stores the parameter 'n' from @var{key} as an unsigned MPI into
+@var{mpi_n}, the parameter 'x' as an opaque MPI into @var{mpi_x}, and
+the parameter 'e' again as an unsigned MPI into @var{mpi_e}.
+
+@var{path} is an optional string used to locate a token.  The
+exclamation mark separated tokens are used via
+@code{gcry_sexp_find_token} to find a start point inside the
+S-expression.
+
+In buffer descriptor mode a pointer to a @code{gcry_buffer_t}
+descriptor is expected instead of a pointer to an MPI.  The caller may
+use two different operation modes here: If the @var{data} field of the
+provided descriptor is @code{NULL}, the function allocates a new
+buffer and stores it at @var{data}; the other fields are set
+accordingly with @var{off} set to 0.  If @var{data} is not
+@code{NULL}, the function assumes that the @var{data}, @var{size}, and
+@var{off} fields specify a buffer where to but the value of the
+respective parameter; on return the @var{len} field receives the
+number of bytes copied to that buffer; in case the buffer is too
+small, the function immediately returns with an error code (and
+@var{len} is set to 0).
+
+The function returns NULL on success.  On error an error code is
+returned and the passed MPIs are either unchanged or set to NULL.
+@end deftypefun
+
 
 @c **********************************************************
 @c *******************  MPIs ******** ***********************
index c1ba2f7..3b09448 100644 (file)
 #define GCC_ATTR_UNUSED
 #endif
 
-#if __GNUC__ >= 4
-# define GCC_ATTR_SENTINEL(a) __attribute__ ((sentinel(a)))
-#else
-# define GCC_ATTR_SENTINEL(a)
-#endif
-
-
 /* Gettext macros.  */
 
 #define _(a)  _gcry_gettext(a)
@@ -382,6 +375,12 @@ gcry_err_code_t _gcry_sexp_vbuild (gcry_sexp_t *retsexp, size_t *erroff,
                                    const char *format, va_list arg_ptr);
 gcry_mpi_t _gcry_sexp_nth_opaque_mpi (gcry_sexp_t list, int number);
 char *_gcry_sexp_nth_string (const gcry_sexp_t list, int number);
+gpg_err_code_t _gcry_sexp_vextract_param (gcry_sexp_t sexp, const char *path,
+                                          const char *list, va_list arg_ptr);
+gpg_err_code_t _gcry_sexp_extract_param (gcry_sexp_t sexp,
+                                         const char *path,
+                                         const char *list,
+                                         ...) _GCRY_GCC_ATTR_SENTINEL(0);
 
 
 /*-- fips.c --*/
index 8646f43..64cc0e4 100644 (file)
@@ -102,6 +102,10 @@ extern "C" {
 
 #define _GCRY_GCC_ATTR_PRINTF(f,a)  __attribute__ ((format (printf,f,a)))
 
+#if _GCRT_GCC_VERSION >= 40000
+#define _GCRY_GCC_ATTR_SENTINEL(a) __attribute__ ((sentinel(a)))
+#endif
+
 #endif /*__GNUC__*/
 
 #ifndef _GCRY_GCC_ATTR_DEPRECATED
@@ -114,7 +118,10 @@ extern "C" {
 #define _GCRY_GCC_ATTR_MALLOC
 #endif
 #ifndef _GCRY_GCC_ATTR_PRINTF
-#define _GCRY_GCC_ATTR_PRINTF
+#define _GCRY_GCC_ATTR_PRINTF(f,a)
+#endif
+#ifndef _GCRY_GCC_ATTR_SENTINEL
+#define _GCRY_GCC_ATTR_SENTINEL(a)
 #endif
 
 /* Make up an attribute to mark functions and types as deprecated but
@@ -459,6 +466,12 @@ char *gcry_sexp_nth_string (gcry_sexp_t list, int number);
    value can't be converted to an MPI, `NULL' is returned.  */
 gcry_mpi_t gcry_sexp_nth_mpi (gcry_sexp_t list, int number, int mpifmt);
 
+/* Convenience fucntion to extract parameters from an S-expression
+ * using a list of single letter parameters.  */
+gpg_error_t gcry_sexp_extract_param (gcry_sexp_t sexp,
+                                     const char *path,
+                                     const char *list,
+                                     ...) _GCRY_GCC_ATTR_SENTINEL(0);
 
 \f
 /*******************************************
index 7efb3b9..ec0c1e3 100644 (file)
@@ -253,5 +253,8 @@ EXPORTS
       gcry_log_debugpnt         @223
       gcry_log_debugsxp         @224
 
+      gcry_sexp_extract_param   @225
+
+
 
 ;; end of file with public symbols for Windows.
index b1669fd..be72aad 100644 (file)
@@ -76,7 +76,7 @@ GCRYPT_1.6 {
     gcry_sexp_new; gcry_sexp_nth; gcry_sexp_nth_buffer; gcry_sexp_nth_data;
     gcry_sexp_nth_mpi; gcry_sexp_prepend; gcry_sexp_release;
     gcry_sexp_sprint; gcry_sexp_sscan; gcry_sexp_vlist;
-    gcry_sexp_nth_string;
+    gcry_sexp_nth_string; gcry_sexp_extract_param;
 
     gcry_mpi_is_neg; gcry_mpi_neg; gcry_mpi_abs;
     gcry_mpi_add; gcry_mpi_add_ui; gcry_mpi_addm; gcry_mpi_aprint;
index 6a2a9be..6e4ff27 100644 (file)
@@ -2117,3 +2117,238 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
        }
     }
 }
+
+
+/* Extract MPIs from an s-expression using a list of one letter
+ * parameters.  The names of these parameters are given by the string
+ * LIST.  Some special characters may be given to control the
+ * conversion:
+ *
+ *    + :: Switch to unsigned integer format (default).
+ *    - :: Switch to standard signed format.
+ *    / :: Switch to opaque format.
+ *    & :: Switch to buffer descriptor mode - see below.
+ *    ? :: The previous parameter is optional.
+ *
+ * Unless in gcry_buffer_t mode for each parameter name a pointer to
+ * an MPI variable is expected and finally a NULL is expected.
+ * Example:
+ *
+ *   _gcry_sexp_extract_param (key, NULL, "n/x+ed",
+ *                             &mpi_n, &mpi_x, &mpi_e, NULL)
+ *
+ * This stores the parameter "N" from KEY as an unsigned MPI into
+ * MPI_N, the parameter "X" as an opaque MPI into MPI_X, and the
+ * parameter "E" again as an unsigned MPI into MPI_E.
+ *
+ * If in buffer descriptor mode a pointer to gcry_buffer_t descriptor
+ * is expected instead of a pointer to an MPI.  The caller may use two
+ * different operation modes: If the DATA field of the provided buffer
+ * descriptor is NULL, the function allocates a new buffer and stores
+ * it at DATA; the other fields are set accordingly with OFF being 0.
+ * If DATA is not NULL, the function assumes that DATA, SIZE, and OFF
+ * describe a buffer where to but the data; on return the LEN field
+ * receives the number of bytes copied to that buffer; if the buffer
+ * is too small, the function immediately returns with an error code
+ * (and LEN set to 0).
+ *
+ * PATH is an optional string used to locate a token.  The exclamation
+ * mark separated tokens are used to via gcry_sexp_find_token to find
+ * a start point inside SEXP.
+ *
+ * The function returns NULL on success.  On error an error code is
+ * returned and the passed MPIs are either unchanged or set to NULL.
+ */
+gpg_err_code_t
+_gcry_sexp_vextract_param (gcry_sexp_t sexp, const char *path,
+                           const char *list, va_list arg_ptr)
+{
+  gpg_err_code_t rc;
+  const char *s;
+  gcry_mpi_t *array[20];
+  char arrayisdesc[20];
+  int idx;
+  gcry_sexp_t l1;
+  int mode = '+'; /* Default to GCRYMPI_FMT_USG.  */
+  gcry_sexp_t freethis = NULL;
+
+  memset (arrayisdesc, 0, sizeof arrayisdesc);
+
+  /* First copy all the args into an array.  This is required so that
+     we are able to release already allocated MPIs if later an error
+     was found.  */
+  for (s=list, idx=0; *s && idx < DIM (array); s++)
+    {
+      if (*s == '&' || *s == '+' || *s == '-' || *s == '/' || *s == '?' )
+        ;
+      else
+        {
+          array[idx] = va_arg (arg_ptr, gcry_mpi_t *);
+          if (!array[idx])
+            return GPG_ERR_MISSING_VALUE; /* NULL pointer given.  */
+          idx++;
+        }
+    }
+  if (*s)
+    return GPG_ERR_LIMIT_REACHED;  /* Too many list elements.  */
+  if (va_arg (arg_ptr, gcry_mpi_t *))
+    return GPG_ERR_INV_ARG;  /* Not enough list elemends.  */
+
+  /* Drill down.  */
+  while (path && *path)
+    {
+      size_t n;
+
+      s = strchr (path, '!');
+      if (s == path)
+        {
+          rc = GPG_ERR_NOT_FOUND;
+          goto cleanup;
+        }
+      n = s? s - path : 0;
+      l1 = gcry_sexp_find_token (sexp, path, n);
+      if (!l1)
+        {
+          rc = GPG_ERR_NOT_FOUND;
+          goto cleanup;
+        }
+      sexp = l1; l1 = NULL;
+      gcry_sexp_release (freethis);
+      freethis = sexp;
+      if (n)
+        path += n + 1;
+      else
+        path = NULL;
+    }
+
+
+  /* Now extract all parameters.  */
+  for (s=list, idx=0; *s; s++)
+    {
+      if (*s == '&' || *s == '+' || *s == '-' || *s == '/')
+        mode = *s;
+      else if (*s == '?')
+        ; /* Only used via lookahead.  */
+      else
+        {
+          l1 = gcry_sexp_find_token (sexp, s, 1);
+          if (!l1 && s[1] == '?')
+            {
+              /* Optional element not found.  */
+              if (mode == '&')
+                {
+                  gcry_buffer_t *spec = (gcry_buffer_t*)array[idx];
+                  if (!spec->data)
+                    {
+                      spec->size = 0;
+                      spec->off = 0;
+                    }
+                  spec->len = 0;
+                }
+              else
+                *array[idx] = NULL;
+            }
+          else if (!l1)
+            {
+              rc = GPG_ERR_NO_OBJ;  /* List element not found.  */
+              goto cleanup;
+            }
+           else
+            {
+              if (mode == '&')
+                {
+                  gcry_buffer_t *spec = (gcry_buffer_t*)array[idx];
+
+                  if (spec->data)
+                    {
+                      const char *pbuf;
+                      size_t nbuf;
+
+                      pbuf = gcry_sexp_nth_data (l1, 1, &nbuf);
+                      if (!pbuf || !nbuf)
+                        {
+                          rc = GPG_ERR_INV_OBJ;
+                          goto cleanup;
+                        }
+                      if (spec->off + nbuf > spec->size)
+                        {
+                          rc = GPG_ERR_BUFFER_TOO_SHORT;
+                          goto cleanup;
+                        }
+                      memcpy ((char*)spec->data + spec->off, pbuf, nbuf);
+                      spec->len = nbuf;
+                      arrayisdesc[idx] = 1;
+                    }
+                  else
+                    {
+                      spec->data = gcry_sexp_nth_buffer (l1, 1, &spec->size);
+                      if (!spec->data)
+                        {
+                          rc = GPG_ERR_INV_OBJ; /* Or out of core.  */
+                          goto cleanup;
+                        }
+                      spec->len = spec->size;
+                      spec->off = 0;
+                      arrayisdesc[idx] = 2;
+                    }
+                }
+              else if (mode == '/')
+                *array[idx] = _gcry_sexp_nth_opaque_mpi (l1, 1);
+              else if (mode == '-')
+                *array[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_STD);
+              else
+                *array[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+              gcry_sexp_release (l1); l1 = NULL;
+              if (!*array[idx])
+                {
+                  rc = GPG_ERR_INV_OBJ;  /* Conversion failed.  */
+                  goto cleanup;
+                }
+            }
+          idx++;
+        }
+    }
+
+  gcry_sexp_release (freethis);
+  return 0;
+
+ cleanup:
+  gcry_sexp_release (freethis);
+  gcry_sexp_release (l1);
+  while (idx--)
+    {
+      if (!arrayisdesc[idx])
+        {
+          gcry_mpi_release (*array[idx]);
+          *array[idx] = NULL;
+        }
+      else if (!arrayisdesc[idx] == 1)
+        {
+          /* Caller provided buffer.  */
+          gcry_buffer_t *spec = (gcry_buffer_t*)array[idx];
+          spec->len = 0;
+        }
+      else
+        {
+          /* We might have allocated a buffer.  */
+          gcry_buffer_t *spec = (gcry_buffer_t*)array[idx];
+          gcry_free (spec->data);
+          spec->data = NULL;
+          spec->size = spec->off = spec->len = 0;
+        }
+     }
+  return rc;
+}
+
+gpg_error_t
+_gcry_sexp_extract_param (gcry_sexp_t sexp, const char *path,
+                          const char *list, ...)
+{
+  gcry_err_code_t rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, list);
+  rc = _gcry_sexp_vextract_param (sexp, path, list, arg_ptr);
+  va_end (arg_ptr);
+  return gpg_error (rc);
+}
index 6e3c755..848925e 100644 (file)
@@ -250,6 +250,21 @@ gcry_sexp_nth_mpi (gcry_sexp_t list, int number, int mpifmt)
   return _gcry_sexp_nth_mpi (list, number, mpifmt);
 }
 
+gpg_error_t
+gcry_sexp_extract_param (gcry_sexp_t sexp, const char *path,
+                         const char *list, ...)
+{
+  gcry_err_code_t rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, list);
+  rc = _gcry_sexp_vextract_param (sexp, path, list, arg_ptr);
+  va_end (arg_ptr);
+  return gpg_error (rc);
+}
+
+
+\f
 gcry_mpi_t
 gcry_mpi_new (unsigned int nbits)
 {
index cd2a60f..1c8f047 100644 (file)
@@ -537,6 +537,7 @@ MARK_VISIBLE (gcry_sexp_release)
 MARK_VISIBLE (gcry_sexp_sprint)
 MARK_VISIBLE (gcry_sexp_sscan)
 MARK_VISIBLE (gcry_sexp_vlist)
+MARK_VISIBLEX(gcry_sexp_extract_param)
 
 MARK_VISIBLEX(gcry_mpi_abs)
 MARK_VISIBLE (gcry_mpi_add)
@@ -615,6 +616,16 @@ MARK_VISIBLEX(_gcry_mpi_get_const)
 
 
 #undef MARK_VISIBLE
-#endif /*_GCRY_INCLUDED_BY_VISIBILITY_C*/
+#else /*!_GCRY_INCLUDED_BY_VISIBILITY_C*/
+
+/* To avoid accidental use of the public functions inside Libgcrypt,
+   we redefine them to catch such errors.  The usual difference
+   between a public and an internal version is that the internal
+   version use gpg_err_code_t and the public version gpg_error_t.  */
+
+#define gcry_sexp_extract_param _gcry_USE_THE_UNDERSCORED_FUNCTION
+
+
+#endif /*!_GCRY_INCLUDED_BY_VISIBILITY_C*/
 
 #endif /*GCRY_VISIBILITY_H*/
index 7c4f7c8..8a6b912 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <assert.h>
 #include "../src/gcrypt-int.h"
 
 #define PGMNAME "tsexp"
 
+#ifndef DIM
+# define DIM(v)                     (sizeof(v)/sizeof((v)[0]))
+#endif
+#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 xstrdup(a)    gcry_xstrdup ((a))
+#define xfree(a)      gcry_free ((a))
+#define pass()        do { ; } while (0)
+
+
 static int verbose;
 static int error_count;
 
 static void
+die (const char *format, ...)
+{
+  va_list arg_ptr ;
+
+  fflush (stdout);
+  fprintf (stderr, "%s: ", PGMNAME);
+  va_start( arg_ptr, format ) ;
+  vfprintf (stderr, format, arg_ptr );
+  va_end(arg_ptr);
+  if (*format && format[strlen(format)-1] != '\n')
+    putc ('\n', stderr);
+  exit (1);
+}
+
+static void
 info (const char *format, ...)
 {
   va_list arg_ptr;
@@ -42,6 +76,8 @@ info (const char *format, ...)
       va_start( arg_ptr, format ) ;
       vfprintf (stderr, format, arg_ptr );
       va_end(arg_ptr);
+      if (*format && format[strlen(format)-1] != '\n')
+        putc ('\n', stderr);
     }
 }
 
@@ -54,10 +90,110 @@ fail ( const char *format, ... )
     va_start( arg_ptr, format ) ;
     vfprintf (stderr, format, arg_ptr );
     va_end(arg_ptr);
+    if (*format && format[strlen(format)-1] != '\n')
+      putc ('\n', stderr);
     error_count++;
 }
 
 
+
+/* 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
+hex2mpi (const char *string)
+{
+  gpg_error_t err;
+  gcry_mpi_t val;
+
+  err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
+  if (err)
+    die ("hex2mpi '%s' failed: %s\n", string, gpg_strerror (err));
+  return val;
+}
+
+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;
+}
+
+
+/* Compare A to B, where B is given as a hex string.  */
+static int
+cmp_mpihex (gcry_mpi_t a, const char *b)
+{
+  gcry_mpi_t bval;
+  int res;
+
+  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;
+}
+
+/* Compare A to B, where A is a buffer and B a hex string.  */
+static int
+cmp_bufhex (const void *a, size_t alen, const char *b)
+{
+  void *bbuf;
+  size_t blen;
+  int res;
+
+  if (!a && !b)
+    return 0;
+  if (a && !b)
+    return 1;
+  if (!a && b)
+    return -1;
+
+  bbuf = hex2buffer (b, &blen);
+  if (!bbuf)
+    die ("cmp_bufhex: error converting hex string\n");
+  if (alen != blen)
+    return alen < blen? -1 : 1;
+  res = memcmp (a, bbuf, alen);
+  xfree (bbuf);
+  return res;
+}
+
+
+\f
 /* fixme: we need better tests */
 static void
 basic (void)
@@ -195,7 +331,7 @@ basic (void)
               fail ("no car for `%s'\n", token);
               continue;
             }
-          info ("car=`%.*s'\n", (int)n, p);
+          /* info ("car=`%.*s'\n", (int)n, p); */
 
           s2 = gcry_sexp_cdr (s1);
           if (!s2)
@@ -230,7 +366,7 @@ basic (void)
                   fail("no car for `%s'\n", parm );
                   continue;
                 }
-              info ("car=`%.*s'\n", (int)n, p);
+              /* info ("car=`%.*s'\n", (int)n, p); */
               p = gcry_sexp_nth_data (s2, 1, &n);
               if (!p)
                 {
@@ -238,7 +374,7 @@ basic (void)
                   fail("no cdr for `%s'\n", parm );
                   continue;
                 }
-              info ("cdr=`%.*s'\n", (int)n, p);
+              /* info ("cdr=`%.*s'\n", (int)n, p); */
 
               a = gcry_sexp_nth_mpi (s2, 0, GCRYMPI_FMT_USG);
               gcry_sexp_release (s2);
@@ -457,6 +593,379 @@ check_sscan (void)
 }
 
 
+static void
+check_extract_param (void)
+{
+  /* This sample data is a real key but with some parameters of the
+     public key modified.  */
+  static char sample1[] =
+    "(key-data"
+    " (public-key"
+    "  (ecc"
+    "   (curve Ed25519)"
+    "   (p #6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED#)"
+    "   (a #EF#)"
+    "   (b #C2036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978B6#)"
+    "   (g #14"
+    "       216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+    "       6666666666666666666666666666666666666666666666666666666666666658#)"
+    "   (n #0000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED#)"
+    "   (q #20B37806015CA06B3AEB9423EE84A41D7F31AA65F4148553755206D679F8BF62#)"
+    "))"
+    " (private-key"
+    "  (ecc"
+    "   (curve Ed25519)"
+    "   (p #7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED#)"
+    "   (a #FF#)"
+    "   (b #D2036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978B6#)"
+    "   (g #04"
+    "       216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+    "       6666666666666666666666666666666666666666666666666666666666666658#)"
+    "   (n #1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED#)"
+    "   (q #30B37806015CA06B3AEB9423EE84A41D7F31AA65F4148553755206D679F8BF62#)"
+    "   (d #56BEA284A22F443A7AEA8CEFA24DA5055CDF1D490C94D8C568FE0802C9169276#)"
+    ")))";
+
+  static char sample1_p[] =
+    "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED";
+  static char sample1_px[] =
+    "6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED";
+  static char sample1_a[] = "FF";
+  static char sample1_ax[] = "EF";
+  static char sample1_b[] =
+    "D2036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978B6";
+  static char sample1_bx[] =
+    "C2036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978B6";
+  static char sample1_g[] =
+    "04"
+    "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+    "6666666666666666666666666666666666666666666666666666666666666658";
+  static char sample1_gx[] =
+    "14"
+    "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+    "6666666666666666666666666666666666666666666666666666666666666658";
+  static char sample1_n[] =
+    "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
+  static char sample1_nx[] =
+    "0000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
+  static char sample1_q[] =
+    "30B37806015CA06B3AEB9423EE84A41D7F31AA65F4148553755206D679F8BF62";
+  static char sample1_qx[] =
+    "20B37806015CA06B3AEB9423EE84A41D7F31AA65F4148553755206D679F8BF62";
+  static char sample1_d[] =
+    "56BEA284A22F443A7AEA8CEFA24DA5055CDF1D490C94D8C568FE0802C9169276";
+
+  static struct {
+    const char *sexp_str;
+    const char *path;
+    const char *list;
+    int nparam;
+    gpg_err_code_t expected_err;
+    const char *exp_p;
+    const char *exp_a;
+    const char *exp_b;
+    const char *exp_g;
+    const char *exp_n;
+    const char *exp_q;
+    const char *exp_d;
+  } tests[] = {
+    {
+      sample1,
+      NULL,
+      "pabgnqd", 6,
+      GPG_ERR_MISSING_VALUE,
+    },
+    {
+      sample1,
+      NULL,
+      "pabgnq", 7,
+      GPG_ERR_INV_ARG
+    },
+    {
+      sample1,
+      NULL,
+      "pabgnqd", 7,
+      0,
+      sample1_px, sample1_ax, sample1_bx, sample1_gx, sample1_nx,
+      sample1_qx, sample1_d
+    },
+    {
+      sample1,
+      NULL,
+      "abg", 3,
+      0,
+      sample1_ax, sample1_bx, sample1_gx
+    },
+    {
+      sample1,
+      NULL,
+      "x?abg", 4,
+      0,
+      NULL, sample1_ax, sample1_bx, sample1_gx
+    },
+    {
+      sample1,
+      NULL,
+      "p?abg", 4,
+      GPG_ERR_USER_1,
+      NULL, sample1_ax, sample1_bx, sample1_gx
+    },
+    {
+      sample1,
+      NULL,
+      "pax?gnqd", 7,
+      0,
+      sample1_px, sample1_ax, NULL, sample1_gx, sample1_nx,
+      sample1_qx, sample1_d
+    },
+    {
+      sample1,
+      "public-key",
+      "pabgnqd", 7,
+      GPG_ERR_NO_OBJ,  /* d is not in public key.  */
+      sample1_px, sample1_ax, sample1_bx, sample1_gx, sample1_nx,
+      sample1_qx, sample1_d
+    },
+    {
+      sample1,
+      "private-key",
+      "pabgnqd", 7,
+      0,
+      sample1_p, sample1_a, sample1_b, sample1_g, sample1_n,
+      sample1_q, sample1_d
+    },
+    {
+      sample1,
+      "public-key!ecc",
+      "pabgnq", 6,
+      0,
+      sample1_px, sample1_ax, sample1_bx, sample1_gx, sample1_nx,
+      sample1_qx
+    },
+    {
+      sample1,
+      "public-key!ecc!foo",
+      "pabgnq", 6,
+      GPG_ERR_NOT_FOUND
+    },
+    {
+      sample1,
+      "public-key!!ecc",
+      "pabgnq", 6,
+      GPG_ERR_NOT_FOUND
+    },
+    {
+      sample1,
+      "private-key",
+      "pa/bgnqd", 7,
+      0,
+      sample1_p, sample1_a, sample1_b, sample1_g, sample1_n,
+      sample1_q, sample1_d
+    },
+    {
+      sample1,
+      "private-key",
+      "p-a+bgnqd", 7,
+      0,
+      sample1_p, "-01", sample1_b, sample1_g, sample1_n,
+      sample1_q, sample1_d
+    },
+    {NULL}
+  };
+  int idx, i;
+  const char *paramstr;
+  int paramidx;
+  gpg_error_t err;
+  gcry_sexp_t sxp;
+  gcry_mpi_t mpis[7];
+  gcry_buffer_t ioarray[7];
+  char iobuffer[200];
+
+  info ("checking gcry_sexp_extract_param\n");
+  for (idx=0; tests[idx].sexp_str; idx++)
+    {
+      err = gcry_sexp_new (&sxp, tests[idx].sexp_str, 0, 1);
+      if (err)
+        die ("converting string to sexp failed: %s", gpg_strerror (err));
+
+      memset (mpis, 0, sizeof mpis);
+      switch (tests[idx].nparam)
+        {
+        case 0:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         NULL);
+          break;
+        case 1:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, NULL);
+          break;
+        case 2:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, NULL);
+          break;
+        case 3:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, mpis+2, NULL);
+          break;
+        case 4:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, mpis+2, mpis+3, NULL);
+          break;
+        case 5:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, mpis+2, mpis+3, mpis+4,
+                                         NULL);
+          break;
+        case 6:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, mpis+2, mpis+3, mpis+4,
+                                         mpis+5, NULL);
+          break;
+        case 7:
+          err = gcry_sexp_extract_param (sxp, tests[idx].path, tests[idx].list,
+                                         mpis+0, mpis+1, mpis+2, mpis+3, mpis+4,
+                                         mpis+5, mpis+6, NULL);
+          break;
+        default:
+          die ("test %d: internal error", idx);
+        }
+
+      if (tests[idx].expected_err
+          && tests[idx].expected_err != GPG_ERR_USER_1)
+        {
+          if (tests[idx].expected_err != gpg_err_code (err))
+            fail ("gcry_sexp_extract_param test %d failed: "
+                  "expected error '%s' - got '%s'", idx,
+                  gpg_strerror (tests[idx].expected_err),gpg_strerror (err));
+
+        }
+      else if (err)
+        {
+          fail ("gcry_sexp_extract_param test %d failed: %s",
+                idx, gpg_strerror (err));
+        }
+      else /* No error - check the extracted values.  */
+        {
+          for (paramidx=0; paramidx < DIM (mpis); paramidx++)
+            {
+              switch (paramidx)
+                {
+                case 0: paramstr = tests[idx].exp_p; break;
+                case 1: paramstr = tests[idx].exp_a; break;
+                case 2: paramstr = tests[idx].exp_b; break;
+                case 3: paramstr = tests[idx].exp_g; break;
+                case 4: paramstr = tests[idx].exp_n; break;
+                case 5: paramstr = tests[idx].exp_q; break;
+                case 6: paramstr = tests[idx].exp_d; break;
+                default:
+                  die ("test %d: internal error: bad param %d",
+                       idx, paramidx);
+                }
+
+              if (tests[idx].expected_err == GPG_ERR_USER_1
+                  && mpis[paramidx] && !paramstr && paramidx == 0)
+                ; /* Okay  Special case error for param 0.  */
+              else if (!mpis[paramidx] && !paramstr)
+                ; /* Okay.  */
+              else if (!mpis[paramidx] && paramstr)
+                fail ("test %d: value for param %d expected but not returned",
+                      idx, paramidx);
+              else if (mpis[paramidx] && !paramstr)
+                fail ("test %d: value for param %d not expected",
+                      idx, paramidx);
+              else if (cmp_mpihex (mpis[paramidx], paramstr))
+                {
+                  fail ("test %d: param %d mismatch", idx, paramidx);
+                  gcry_log_debug    ("expected: %s\n", paramstr);
+                  gcry_log_debugmpi ("     got", mpis[paramidx]);
+                }
+              else if (tests[idx].expected_err && paramidx == 0)
+                fail ("test %d: param %d: expected error '%s' - got 'Success'",
+                      idx, paramidx, gpg_strerror (tests[idx].expected_err));
+            }
+
+        }
+
+      for (i=0; i < DIM (mpis); i++)
+        gcry_mpi_release (mpis[i]);
+      gcry_sexp_release (sxp);
+    }
+
+  info ("checking gcry_sexp_extract_param/desc\n");
+
+  memset (ioarray, 0, sizeof ioarray);
+
+  err = gcry_sexp_new (&sxp, sample1, 0, 1);
+  if (err)
+    die ("converting string to sexp failed: %s", gpg_strerror (err));
+
+  ioarray[1].size = sizeof iobuffer;
+  ioarray[1].data = iobuffer;
+  ioarray[1].off  = 0;
+  ioarray[2].size = sizeof iobuffer;
+  ioarray[2].data = iobuffer;
+  ioarray[2].off  = 50;
+  assert (ioarray[2].off < sizeof iobuffer);
+  err = gcry_sexp_extract_param (sxp, "key-data!private-key", "&pab",
+                                 ioarray+0, ioarray+1, ioarray+2, NULL);
+  if (err)
+    fail ("gcry_sexp_extract_param with desc failed: %s", gpg_strerror (err));
+  else
+    {
+      if (!ioarray[0].data)
+        fail ("gcry_sexp_extract_param/desc failed: no P");
+      else if (ioarray[0].size != 32)
+        fail ("gcry_sexp_extract_param/desc failed: P has wrong size");
+      else if (ioarray[0].len != 32)
+        fail ("gcry_sexp_extract_param/desc failed: P has wrong length");
+      else if (ioarray[0].off)
+        fail ("gcry_sexp_extract_param/desc failed: P has OFF set");
+      else if (cmp_bufhex (ioarray[0].data, ioarray[0].len, sample1_p))
+        {
+          fail ("gcry_sexp_extract_param/desc failed: P mismatch");
+          gcry_log_debug    ("expected: %s\n", sample1_p);
+          gcry_log_debughex ("     got", ioarray[0].data, ioarray[0].len);
+        }
+
+      if (!ioarray[1].data)
+        fail ("gcry_sexp_extract_param/desc failed: A buffer lost");
+      else if (ioarray[1].size != sizeof iobuffer)
+        fail ("gcry_sexp_extract_param/desc failed: A size changed");
+      else if (ioarray[1].off != 0)
+        fail ("gcry_sexp_extract_param/desc failed: A off changed");
+      else if (ioarray[1].len != 1)
+        fail ("gcry_sexp_extract_param/desc failed: A has wrong length");
+      else if (cmp_bufhex (ioarray[1].data + ioarray[1].off, ioarray[1].len,
+                           sample1_a))
+        {
+          fail ("gcry_sexp_extract_param/desc failed: A mismatch");
+          gcry_log_debug    ("expected: %s\n", sample1_a);
+          gcry_log_debughex ("     got",
+                             ioarray[1].data + ioarray[1].off, ioarray[1].len);
+        }
+
+      if (!ioarray[2].data)
+        fail ("gcry_sexp_extract_param/desc failed: B buffer lost");
+      else if (ioarray[2].size != sizeof iobuffer)
+        fail ("gcry_sexp_extract_param/desc failed: B size changed");
+      else if (ioarray[2].off != 50)
+        fail ("gcry_sexp_extract_param/desc failed: B off changed");
+      else if (ioarray[2].len != 32)
+        fail ("gcry_sexp_extract_param/desc failed: B has wrong length");
+      else if (cmp_bufhex (ioarray[2].data + ioarray[2].off, ioarray[2].len,
+                           sample1_b))
+        {
+          fail ("gcry_sexp_extract_param/desc failed: B mismatch");
+          gcry_log_debug    ("expected: %s\n", sample1_b);
+          gcry_log_debughex ("     got",
+                             ioarray[2].data + ioarray[2].off, ioarray[2].len);
+        }
+
+      xfree (ioarray[0].data);
+    }
+
+  gcry_sexp_release (sxp);
+}
 
 
 int
@@ -472,6 +981,7 @@ main (int argc, char **argv)
   canon_len ();
   back_and_forth ();
   check_sscan ();
+  check_extract_param ();
 
   return error_count? 1:0;
 }