cipher: Improve error handling.
[libgcrypt.git] / src / sexp.c
index 6a2a9be..d063962 100644 (file)
@@ -1,7 +1,7 @@
 /* sexp.c  -  S-Expression handling
  * Copyright (C) 1999, 2000, 2001, 2002, 2003,
  *               2004, 2006, 2007, 2008, 2011  Free Software Foundation, Inc.
- * Copyright (C) 2013 g10 Code GmbH
+ * Copyright (C) 2013, 2014 g10 Code GmbH
  *
  * This file is part of Libgcrypt.
  *
 #define GCRYPT_NO_MPI_MACROS 1
 #include "g10lib.h"
 
-typedef struct gcry_sexp *NODE;
+
+/* Notes on the internal memory layout.
+
+   We store an S-expression as one memory buffer with tags, length and
+   value.  The simplest list would thus be:
+
+   /----------+----------+---------+------+-----------+----------\
+   | open_tag | data_tag | datalen | data | close_tag | stop_tag |
+   \----------+----------+---------+------+-----------+----------/
+
+   Expressed more compact and with an example:
+
+   /----+----+----+---+----+----\
+   | OT | DT | DL | D | CT | ST |  "(foo)"
+   \----+----+----+---+----+----/
+
+   The open tag must always be the first tag of a list as requires by
+   the S-expression specs.  At least data element (data_tag, datalen,
+   data) is required as well.  The close_tag finishes the list and
+   would actually be sufficient.  For fail-safe reasons a final stop
+   tag is always the last byte in a buffer; it has a value of 0 so
+   that string function accidentally applied to an S-expression will
+   never access unallocated data.  We do not support display hints and
+   thus don't need to represent them.  A list may have more an
+   arbitrary number of data elements but at least one is required.
+   The length of each data must be greater than 0 and has a current
+   limit to 65535 bytes (by means of the DATALEN type).
+
+   A list with two data elements:
+
+   /----+----+----+---+----+----+---+----+----\
+   | OT | DT | DL | D | DT | DL | D | CT | ST |  "(foo bar)"
+   \----+----+----+---+----+----+---+----+----/
+
+   In the above example both DL fields have a value of 3.
+   A list of a list with one data element:
+
+   /----+----+----+----+---+----+----+----\
+   | OT | OT | DT | DL | D | CT | CT | ST |  "((foo))"
+   \----+----+----+----+---+----+----+----/
+
+   A list with one element followed by another list:
+
+   /----+----+----+---+----+----+----+---+----+----+----\
+   | OT | DT | DL | D | OT | DT | DL | D | CT | CT | ST |  "(foo (bar))"
+   \----+----+----+---+----+----+----+---+----+----+----/
+
+ */
+
 typedef unsigned short DATALEN;
 
 struct gcry_sexp
@@ -42,11 +90,11 @@ struct gcry_sexp
 
 #define ST_STOP  0
 #define ST_DATA  1  /* datalen follows */
-#define ST_HINT  2  /* datalen follows */
+/*#define ST_HINT  2   datalen follows (currently not used) */
 #define ST_OPEN  3
 #define ST_CLOSE 4
 
-/* the atoi macros assume that the buffer has only valid digits */
+/* The atoi macros assume that the buffer has only valid digits.  */
 #define atoi_1(p)   (*(p) - '0' )
 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
@@ -55,14 +103,14 @@ struct gcry_sexp
 #define TOKEN_SPECIALS  "-./_:*+="
 
 static gcry_err_code_t
-vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
-            const char *buffer, size_t length, int argflag,
-            void **arg_list, va_list arg_ptr);
+do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+                const char *buffer, size_t length, int argflag,
+                void **arg_list, va_list arg_ptr);
 
 static gcry_err_code_t
-sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
-           const char *buffer, size_t length, int argflag,
-           void **arg_list, ...);
+do_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+               const char *buffer, size_t length, int argflag,
+               void **arg_list, ...);
 
 /* Return true if P points to a byte containing a whitespace according
    to the S-expressions definition. */
@@ -123,7 +171,7 @@ dump_string (const byte *p, size_t n, int delim )
 
 
 void
-gcry_sexp_dump (const gcry_sexp_t a)
+_gcry_sexp_dump (const gcry_sexp_t a)
 {
   const byte *p;
   int indent = 0;
@@ -167,9 +215,10 @@ gcry_sexp_dump (const gcry_sexp_t a)
     }
 }
 
-/****************
- * Pass list through except when it is an empty list - in that case
- * return NULL and release the passed list.
+
+/* Pass list through except when it is an empty list - in that case
+ * return NULL and release the passed list.  This is used to make sure
+ * that no forbidden empty lists are created.
  */
 static gcry_sexp_t
 normalize ( gcry_sexp_t list )
@@ -182,13 +231,13 @@ normalize ( gcry_sexp_t list )
   if ( *p == ST_STOP )
     {
       /* this is "" */
-      gcry_sexp_release ( list );
+      sexp_release ( list );
       return NULL;
     }
   if ( *p == ST_OPEN && p[1] == ST_CLOSE )
     {
       /* this is "()" */
-      gcry_sexp_release ( list );
+      sexp_release ( list );
       return NULL;
     }
 
@@ -211,7 +260,7 @@ normalize ( gcry_sexp_t list )
    This function returns 0 and and the pointer to the new object in
    RETSEXP or an error code in which case RETSEXP is set to NULL.  */
 gcry_err_code_t
-gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
+_gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
                   int autodetect, void (*freefnc)(void*) )
 {
   gcry_err_code_t errcode;
@@ -226,7 +275,7 @@ gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
   if (!length && !autodetect)
     { /* What a brave caller to assume that there is really a canonical
          encoded S-expression in buffer */
-      length = gcry_sexp_canon_len (buffer, 0, NULL, &errcode);
+      length = _gcry_sexp_canon_len (buffer, 0, NULL, &errcode);
       if (!length)
         return errcode;
     }
@@ -235,7 +284,7 @@ gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
       length = strlen ((char *)buffer);
     }
 
-  errcode = sexp_sscan (&se, NULL, buffer, length, 0, NULL);
+  errcode = do_sexp_sscan (&se, NULL, buffer, length, 0, NULL);
   if (errcode)
     return errcode;
 
@@ -254,10 +303,10 @@ gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
 
 /* Same as gcry_sexp_create but don't transfer ownership */
 gcry_err_code_t
-gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
+_gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
                int autodetect)
 {
-  return gcry_sexp_create (retsexp, (void *)buffer, length, autodetect, NULL);
+  return _gcry_sexp_create (retsexp, (void *)buffer, length, autodetect, NULL);
 }
 
 
@@ -265,11 +314,11 @@ gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
  * Release resource of the given SEXP object.
  */
 void
-gcry_sexp_release( gcry_sexp_t sexp )
+_gcry_sexp_release( gcry_sexp_t sexp )
 {
   if (sexp)
     {
-      if (gcry_is_secure (sexp))
+      if (_gcry_is_secure (sexp))
         {
           /* Extra paranoid wiping. */
           const byte *p = sexp->d;
@@ -298,7 +347,7 @@ gcry_sexp_release( gcry_sexp_t sexp )
             }
           wipememory (sexp->d, p - sexp->d);
         }
-      gcry_free ( sexp );
+      xfree ( sexp );
     }
 }
 
@@ -309,7 +358,7 @@ gcry_sexp_release( gcry_sexp_t sexp )
  * element straight into the new pair.
  */
 gcry_sexp_t
-gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
+_gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
 {
   (void)a;
   (void)b;
@@ -326,7 +375,7 @@ gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
  * with a NULL.
  */
 gcry_sexp_t
-gcry_sexp_alist( const gcry_sexp_t *array )
+_gcry_sexp_alist( const gcry_sexp_t *array )
 {
   (void)array;
 
@@ -340,7 +389,7 @@ gcry_sexp_alist( const gcry_sexp_t *array )
  * Make a list from all items, the end of list is indicated by a NULL
  */
 gcry_sexp_t
-gcry_sexp_vlist( const gcry_sexp_t a, ... )
+_gcry_sexp_vlist( const gcry_sexp_t a, ... )
 {
   (void)a;
   /* NYI: Implementation should be quite easy with our new data
@@ -355,7 +404,7 @@ gcry_sexp_vlist( const gcry_sexp_t a, ... )
  * Returns: a new ist (which maybe a)
  */
 gcry_sexp_t
-gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
+_gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
 {
   (void)a;
   (void)n;
@@ -366,7 +415,7 @@ gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
 }
 
 gcry_sexp_t
-gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
+_gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
 {
   (void)a;
   (void)n;
@@ -383,7 +432,7 @@ gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
  * Returns: A new list with this sublist or NULL if not found.
  */
 gcry_sexp_t
-gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
+_gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
 {
   const byte *p;
   DATALEN n;
@@ -434,7 +483,7 @@ gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
                }
               n = p - head;
 
-              newlist = gcry_malloc ( sizeof *newlist + n );
+              newlist = xtrymalloc ( sizeof *newlist + n );
               if (!newlist)
                 {
                   /* No way to return an error code, so we can only
@@ -463,7 +512,7 @@ gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
  * Return the length of the given list
  */
 int
-gcry_sexp_length (const gcry_sexp_t list)
+_gcry_sexp_length (const gcry_sexp_t list)
 {
   const byte *p;
   DATALEN n;
@@ -501,7 +550,7 @@ gcry_sexp_length (const gcry_sexp_t list)
 
 
 /* Return the internal lengths offset of LIST.  That is the size of
-   the buffer from the first ST_OPEN, which is retruned at R_OFF, to
+   the buffer from the first ST_OPEN, which is returned at R_OFF, to
    the corresponding ST_CLOSE inclusive.  */
 static size_t
 get_internal_buffer (const gcry_sexp_t list, size_t *r_off)
@@ -542,10 +591,10 @@ get_internal_buffer (const gcry_sexp_t list, size_t *r_off)
 
 
 
-/* Extract the CAR of the given list.  May return NULL for bad lists
-   or memory failure.  */
+/* Extract the n-th element of the given LIST.  Returns NULL for
+   no-such-element, a corrupt list, or memory failure.  */
 gcry_sexp_t
-gcry_sexp_nth (const gcry_sexp_t list, int number)
+_gcry_sexp_nth (const gcry_sexp_t list, int number)
 {
   const byte *p;
   DATALEN n;
@@ -587,15 +636,16 @@ gcry_sexp_nth (const gcry_sexp_t list, int number)
 
   if (*p == ST_DATA)
     {
-      memcpy (&n, p, sizeof n);
-      p += sizeof n;
-      newlist = gcry_malloc (sizeof *newlist + n + 1);
+      memcpy (&n, p+1, sizeof n);
+      newlist = xtrymalloc (sizeof *newlist + 1 + 1 + sizeof n + n + 1);
       if (!newlist)
         return NULL;
       d = newlist->d;
-      memcpy (d, p, n);
-      d += n;
-      *d++ = ST_STOP;
+      *d++ = ST_OPEN;
+      memcpy (d, p, 1 + sizeof n + n);
+      d += 1 + sizeof n + n;
+      *d++ = ST_CLOSE;
+      *d = ST_STOP;
     }
   else if (*p == ST_OPEN)
     {
@@ -625,7 +675,7 @@ gcry_sexp_nth (const gcry_sexp_t list, int number)
       } while (level);
       n = p + 1 - head;
 
-      newlist = gcry_malloc (sizeof *newlist + n);
+      newlist = xtrymalloc (sizeof *newlist + n);
       if (!newlist)
         return NULL;
       d = newlist->d;
@@ -639,17 +689,18 @@ gcry_sexp_nth (const gcry_sexp_t list, int number)
   return normalize (newlist);
 }
 
+
 gcry_sexp_t
-gcry_sexp_car (const gcry_sexp_t list)
+_gcry_sexp_car (const gcry_sexp_t list)
 {
-  return gcry_sexp_nth (list, 0);
+  return _gcry_sexp_nth (list, 0);
 }
 
 
 /* Helper to get data from the car.  The returned value is valid as
    long as the list is not modified. */
 static const char *
-sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen)
+do_sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen)
 {
   const byte *p;
   DATALEN n;
@@ -708,9 +759,9 @@ sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen)
 /* Get data from the car.  The returned value is valid as long as the
    list is not modified.  */
 const char *
-gcry_sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen )
+_gcry_sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen )
 {
-  return sexp_nth_data (list, number, datalen);
+  return do_sexp_nth_data (list, number, datalen);
 }
 
 
@@ -719,17 +770,17 @@ gcry_sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen )
    the caller.  This is basically the same as gcry_sexp_nth_data but
    with an allocated result. */
 void *
-gcry_sexp_nth_buffer (const gcry_sexp_t list, int number, size_t *rlength)
+_gcry_sexp_nth_buffer (const gcry_sexp_t list, int number, size_t *rlength)
 {
   const char *s;
   size_t n;
   char *buf;
 
   *rlength = 0;
-  s = sexp_nth_data (list, number, &n);
+  s = do_sexp_nth_data (list, number, &n);
   if (!s || !n)
     return NULL;
-  buf = gcry_malloc (n);
+  buf = xtrymalloc (n);
   if (!buf)
     return NULL;
   memcpy (buf, s, n);
@@ -741,16 +792,16 @@ gcry_sexp_nth_buffer (const gcry_sexp_t list, int number, size_t *rlength)
 /* Get a string from the car.  The returned value is a malloced string
    and needs to be freed by the caller.  */
 char *
-gcry_sexp_nth_string (const gcry_sexp_t list, int number)
+_gcry_sexp_nth_string (const gcry_sexp_t list, int number)
 {
   const char *s;
   size_t n;
   char *buf;
 
-  s = sexp_nth_data (list, number, &n);
+  s = do_sexp_nth_data (list, number, &n);
   if (!s || n < 1 || (n+1) < 1)
     return NULL;
-  buf = gcry_malloc (n+1);
+  buf = xtrymalloc (n+1);
   if (!buf)
     return NULL;
   memcpy (buf, s, n);
@@ -763,45 +814,39 @@ gcry_sexp_nth_string (const gcry_sexp_t list, int number)
  * Get a MPI from the car
  */
 gcry_mpi_t
-gcry_sexp_nth_mpi (gcry_sexp_t list, int number, int mpifmt)
+_gcry_sexp_nth_mpi (gcry_sexp_t list, int number, int mpifmt)
 {
-  const char *s;
   size_t n;
   gcry_mpi_t a;
 
-  if ( !mpifmt )
-    mpifmt = GCRYMPI_FMT_STD;
-
-  s = sexp_nth_data (list, number, &n);
-  if (!s)
-    return NULL;
+  if (mpifmt == GCRYMPI_FMT_OPAQUE)
+    {
+      char *p;
 
-  if ( gcry_mpi_scan ( &a, mpifmt, s, n, NULL ) )
-    return NULL;
+      p = _gcry_sexp_nth_buffer (list, number, &n);
+      if (!p)
+        return NULL;
 
-  return a;
-}
+      a = _gcry_is_secure (list)? _gcry_mpi_snew (0) : _gcry_mpi_new (0);
+      if (a)
+        mpi_set_opaque (a, p, n*8);
+      else
+        xfree (p);
+    }
+  else
+    {
+      const char *s;
 
+      if (!mpifmt)
+        mpifmt = GCRYMPI_FMT_STD;
 
-/*
- * Get data from the car and store return it as an opaque MPI.
- */
-gcry_mpi_t
-_gcry_sexp_nth_opaque_mpi (gcry_sexp_t list, int number)
-{
-  char *p;
-  size_t n;
-  gcry_mpi_t a;
-
-  p = gcry_sexp_nth_buffer (list, number, &n);
-  if (!p)
-    return NULL;
+      s = do_sexp_nth_data (list, number, &n);
+      if (!s)
+        return NULL;
 
-  a = gcry_is_secure (list)? _gcry_mpi_snew (0) : _gcry_mpi_new (0);
-  if (a)
-    gcry_mpi_set_opaque (a, p, n*8);
-  else
-    gcry_free (p);
+      if (_gcry_mpi_scan (&a, mpifmt, s, n, NULL))
+        return NULL;
+    }
 
   return a;
 }
@@ -811,7 +856,7 @@ _gcry_sexp_nth_opaque_mpi (gcry_sexp_t list, int number)
  * Get the CDR
  */
 gcry_sexp_t
-gcry_sexp_cdr(const gcry_sexp_t list)
+_gcry_sexp_cdr(const gcry_sexp_t list)
 {
   const byte *p;
   const byte *head;
@@ -878,7 +923,7 @@ gcry_sexp_cdr(const gcry_sexp_t list)
   } while (level);
   n = p - head;
 
-  newlist = gcry_malloc (sizeof *newlist + n + 2);
+  newlist = xtrymalloc (sizeof *newlist + n + 2);
   if (!newlist)
     return NULL;
   d = newlist->d;
@@ -893,13 +938,13 @@ gcry_sexp_cdr(const gcry_sexp_t list)
 
 
 gcry_sexp_t
-gcry_sexp_cadr ( const gcry_sexp_t list )
+_gcry_sexp_cadr ( const gcry_sexp_t list )
 {
   gcry_sexp_t a, b;
 
-  a = gcry_sexp_cdr (list);
-  b = gcry_sexp_car (a);
-  gcry_sexp_release (a);
+  a = _gcry_sexp_cdr (list);
+  b = _gcry_sexp_car (a);
+  sexp_release (a);
   return b;
 }
 
@@ -940,7 +985,7 @@ make_space ( struct make_space_ctx *c, size_t n )
       newsize = c->allocated + 2*(n+sizeof(DATALEN)+1);
       if (newsize <= c->allocated)
         return GPG_ERR_TOO_LARGE;
-      newsexp = gcry_realloc ( c->sexp, sizeof *newsexp + newsize - 1);
+      newsexp = xtryrealloc ( c->sexp, sizeof *newsexp + newsize - 1);
       if (!newsexp)
         return gpg_err_code_from_errno (errno);
       c->allocated = newsize;
@@ -1048,9 +1093,9 @@ unquote_string (const char *string, size_t length, unsigned char *buf)
  * regardless whether it is needed or not.
  */
 static gpg_err_code_t
-vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
-            const char *buffer, size_t length, int argflag,
-            void **arg_list, va_list arg_ptr)
+do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+                const char *buffer, size_t length, int argflag,
+                void **arg_list, va_list arg_ptr)
 {
   gcry_err_code_t err = 0;
   static const char tokenchars[] =
@@ -1074,6 +1119,13 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
   int arg_counter = 0;
   int level = 0;
 
+  if (!retsexp)
+    return GPG_ERR_INV_ARG;
+  *retsexp = NULL;
+
+  if (!buffer)
+    return GPG_ERR_INV_ARG;
+
   if (!erroff)
     erroff = &dummy_erroff;
 
@@ -1115,10 +1167,10 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
      the provided one.  However, we add space for one extra datalen so
      that the code which does the ST_CLOSE can use MAKE_SPACE */
   c.allocated = length + sizeof(DATALEN);
-  if (buffer && length && gcry_is_secure (buffer))
-    c.sexp = gcry_malloc_secure (sizeof *c.sexp + c.allocated - 1);
+  if (length && _gcry_is_secure (buffer))
+    c.sexp = xtrymalloc_secure (sizeof *c.sexp + c.allocated - 1);
   else
-    c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1);
+    c.sexp = xtrymalloc (sizeof *c.sexp + c.allocated - 1);
   if (!c.sexp)
     {
       err = gpg_err_code_from_errno (errno);
@@ -1338,25 +1390,25 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
 
              ARG_NEXT (m, gcry_mpi_t);
 
-              if (gcry_mpi_get_flag (m, GCRYMPI_FLAG_OPAQUE))
+              if (mpi_get_flag (m, GCRYMPI_FLAG_OPAQUE))
                 {
                   void *mp;
                   unsigned int nbits;
 
-                  mp = gcry_mpi_get_opaque (m, &nbits);
+                  mp = mpi_get_opaque (m, &nbits);
                   nm = (nbits+7)/8;
                   if (mp && nm)
                     {
                       MAKE_SPACE (nm);
-                      if (!gcry_is_secure (c.sexp->d)
-                          && gcry_mpi_get_flag (m, GCRYMPI_FLAG_SECURE))
+                      if (!_gcry_is_secure (c.sexp->d)
+                          && mpi_get_flag (m, GCRYMPI_FLAG_SECURE))
                         {
                           /* We have to switch to secure allocation.  */
                           gcry_sexp_t newsexp;
                           byte *newhead;
 
-                          newsexp = gcry_malloc_secure (sizeof *newsexp
-                                                        + c.allocated - 1);
+                          newsexp = xtrymalloc_secure (sizeof *newsexp
+                                                       + c.allocated - 1);
                           if (!newsexp)
                             {
                               err = gpg_err_code_from_errno (errno);
@@ -1365,7 +1417,7 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                           newhead = newsexp->d;
                           memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
                           c.pos = newhead + (c.pos - c.sexp->d);
-                          gcry_free (c.sexp);
+                          xfree (c.sexp);
                           c.sexp = newsexp;
                         }
 
@@ -1377,19 +1429,19 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                 }
               else
                 {
-                  if (gcry_mpi_print (mpifmt, NULL, 0, &nm, m))
+                  if (_gcry_mpi_print (mpifmt, NULL, 0, &nm, m))
                     BUG ();
 
                   MAKE_SPACE (nm);
-                  if (!gcry_is_secure (c.sexp->d)
-                      && gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE))
+                  if (!_gcry_is_secure (c.sexp->d)
+                      && mpi_get_flag ( m, GCRYMPI_FLAG_SECURE))
                     {
                       /* We have to switch to secure allocation.  */
                       gcry_sexp_t newsexp;
                       byte *newhead;
 
-                      newsexp = gcry_malloc_secure (sizeof *newsexp
-                                                    + c.allocated - 1);
+                      newsexp = xtrymalloc_secure (sizeof *newsexp
+                                                   + c.allocated - 1);
                       if (!newsexp)
                         {
                           err = gpg_err_code_from_errno (errno);
@@ -1398,13 +1450,13 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                       newhead = newsexp->d;
                       memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
                       c.pos = newhead + (c.pos - c.sexp->d);
-                      gcry_free (c.sexp);
+                      xfree (c.sexp);
                       c.sexp = newsexp;
                     }
 
                   *c.pos++ = ST_DATA;
                   STORE_LEN (c.pos, nm);
-                  if (gcry_mpi_print (mpifmt, c.pos, nm, &nm, m))
+                  if (_gcry_mpi_print (mpifmt, c.pos, nm, &nm, m))
                     BUG ();
                   c.pos += nm;
                 }
@@ -1435,15 +1487,15 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
 
              MAKE_SPACE (alen);
              if (alen
-                  && !gcry_is_secure (c.sexp->d)
-                 && gcry_is_secure (astr))
+                  && !_gcry_is_secure (c.sexp->d)
+                 && _gcry_is_secure (astr))
               {
                  /* We have to switch to secure allocation.  */
                  gcry_sexp_t newsexp;
                  byte *newhead;
 
-                 newsexp = gcry_malloc_secure (sizeof *newsexp
-                                                + c.allocated - 1);
+                 newsexp = xtrymalloc_secure (sizeof *newsexp
+                                               + c.allocated - 1);
                   if (!newsexp)
                     {
                       err = gpg_err_code_from_errno (errno);
@@ -1452,7 +1504,7 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                  newhead = newsexp->d;
                  memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
                  c.pos = newhead + (c.pos - c.sexp->d);
-                 gcry_free (c.sexp);
+                 xfree (c.sexp);
                  c.sexp = newsexp;
                }
 
@@ -1633,12 +1685,10 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
       if (c.sexp)
         {
           /* Extra paranoid wipe on error. */
-          if (gcry_is_secure (c.sexp))
+          if (_gcry_is_secure (c.sexp))
             wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1);
-          gcry_free (c.sexp);
+          xfree (c.sexp);
         }
-      /* This might be expected by existing code...  */
-      *retsexp = NULL;
     }
   else
     *retsexp = normalize (c.sexp);
@@ -1650,16 +1700,16 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
 
 
 static gpg_err_code_t
-sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
-           const char *buffer, size_t length, int argflag,
-           void **arg_list, ...)
+do_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+               const char *buffer, size_t length, int argflag,
+               void **arg_list, ...)
 {
   gcry_err_code_t rc;
   va_list arg_ptr;
 
   va_start (arg_ptr, arg_list);
-  rc = vsexp_sscan (retsexp, erroff, buffer, length, argflag,
-                   arg_list, arg_ptr);
+  rc = do_vsexp_sscan (retsexp, erroff, buffer, length, argflag,
+                       arg_list, arg_ptr);
   va_end (arg_ptr);
 
   return rc;
@@ -1667,14 +1717,14 @@ sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
 
 
 gpg_err_code_t
-gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
+_gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
 {
   gcry_err_code_t rc;
   va_list arg_ptr;
 
   va_start (arg_ptr, format);
-  rc = vsexp_sscan (retsexp, erroff, format, strlen(format), 1,
-                   NULL, arg_ptr);
+  rc = do_vsexp_sscan (retsexp, erroff, format, strlen(format), 1,
+                       NULL, arg_ptr);
   va_end (arg_ptr);
 
   return rc;
@@ -1685,26 +1735,26 @@ gcry_err_code_t
 _gcry_sexp_vbuild (gcry_sexp_t *retsexp, size_t *erroff,
                    const char *format, va_list arg_ptr)
 {
-  return vsexp_sscan (retsexp, erroff, format, strlen(format), 1,
-                     NULL, arg_ptr);
+  return do_vsexp_sscan (retsexp, erroff, format, strlen(format), 1,
+                         NULL, arg_ptr);
 }
 
 
 /* Like gcry_sexp_build, but uses an array instead of variable
    function arguments.  */
 gcry_err_code_t
-gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
-                      const char *format, void **arg_list)
+_gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
+                        const char *format, void **arg_list)
 {
-  return sexp_sscan (retsexp, erroff, format, strlen(format), 1, arg_list);
+  return do_sexp_sscan (retsexp, erroff, format, strlen(format), 1, arg_list);
 }
 
 
 gcry_err_code_t
-gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
-                const char *buffer, size_t length)
+_gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+                  const char *buffer, size_t length)
 {
-  return sexp_sscan (retsexp, erroff, buffer, length, 0, NULL);
+  return do_sexp_sscan (retsexp, erroff, buffer, length, 0, NULL);
 }
 
 \f
@@ -1837,8 +1887,8 @@ convert_to_token (const unsigned char *src, size_t len, char *dest)
  * the required length is returned.
  */
 size_t
-gcry_sexp_sprint (const gcry_sexp_t list, int mode,
-                  void *buffer, size_t maxlength )
+_gcry_sexp_sprint (const gcry_sexp_t list, int mode,
+                   void *buffer, size_t maxlength )
 {
   static unsigned char empty[3] = { ST_OPEN, ST_CLOSE, ST_STOP };
   const unsigned char *s;
@@ -1988,8 +2038,8 @@ gcry_sexp_sprint (const gcry_sexp_t list, int mode,
    data passed from outside. errorcode and erroff may both be passed as
    NULL.  */
 size_t
-gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
-                     size_t *erroff, gcry_err_code_t *errcode)
+_gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
+                      size_t *erroff, gcry_err_code_t *errcode)
 {
   const unsigned char *p;
   const unsigned char *disphint = NULL;
@@ -2117,3 +2167,271 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
        }
     }
 }
+
+
+/* Extract MPIs from an s-expression using a list of 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.
+ *
+ * In general parameter names are single letters.  To use a string for
+ * a parameter name, enclose the name in single quotes.
+ *
+ * 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, *s2;
+  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 if (whitespacep (s))
+        ;
+      else
+        {
+          if (*s == '\'')
+            {
+              s++;
+              s2 = strchr (s, '\'');
+              if (!s2 || s2 == s)
+                {
+                  /* Closing quote not found or empty string.  */
+                  return GPG_ERR_SYNTAX;
+                }
+              s = s2;
+            }
+          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;
+      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 (whitespacep (s))
+        ;
+      else if (*s == '?')
+        ; /* Only used via lookahead.  */
+      else
+        {
+          if (*s == '\'')
+            {
+              /* Find closing quote, find token, set S to closing quote.  */
+              s++;
+              s2 = strchr (s, '\'');
+              if (!s2 || s2 == s)
+                {
+                  /* Closing quote not found or empty string.  */
+                  rc = GPG_ERR_SYNTAX;
+                  goto cleanup;
+                }
+              l1 = _gcry_sexp_find_token (sexp, s, s2 - s);
+              s = s2;
+            }
+          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_mpi (l1, 1, GCRYMPI_FMT_OPAQUE);
+              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);
+              sexp_release (l1); l1 = NULL;
+              if (!*array[idx])
+                {
+                  rc = GPG_ERR_INV_OBJ;  /* Conversion failed.  */
+                  goto cleanup;
+                }
+            }
+          idx++;
+        }
+    }
+
+  sexp_release (freethis);
+  return 0;
+
+ cleanup:
+  sexp_release (freethis);
+  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];
+          xfree (spec->data);
+          spec->data = NULL;
+          spec->size = spec->off = spec->len = 0;
+        }
+     }
+  return rc;
+}
+
+gpg_err_code_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 rc;
+}