/* sexp.c - S-Expression handling
* Copyright (C) 1999, 2000, 2001, 2002, 2003,
- * 2004, 2006, 2007, 2008 Free Software Foundation, Inc.
+ * 2004, 2006, 2007, 2008, 2011 Free Software Foundation, Inc.
+ * 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
#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))
#define TOKEN_SPECIALS "-./_:*+="
-static gcry_error_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);
+static gcry_err_code_t
+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_error_t
-sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
- const char *buffer, size_t length, int argflag,
- void **arg_list, ...);
+static gcry_err_code_t
+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. */
void
-gcry_sexp_dump (const gcry_sexp_t a)
+_gcry_sexp_dump (const gcry_sexp_t a)
{
const byte *p;
int indent = 0;
}
}
-/****************
- * 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 )
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;
}
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_error_t
-gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
+gcry_err_code_t
+_gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
int autodetect, void (*freefnc)(void*) )
{
- gcry_error_t errcode;
+ gcry_err_code_t errcode;
gcry_sexp_t se;
if (!retsexp)
- return gcry_error (GPG_ERR_INV_ARG);
+ return GPG_ERR_INV_ARG;
*retsexp = NULL;
if (autodetect < 0 || autodetect > 1 || !buffer)
- return gcry_error (GPG_ERR_INV_ARG);
+ return GPG_ERR_INV_ARG;
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;
}
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;
GCRYSEXP object and use the BUFFER directly. */
freefnc (buffer);
}
- return gcry_error (GPG_ERR_NO_ERROR);
+ return 0;
}
/* Same as gcry_sexp_create but don't transfer ownership */
-gcry_error_t
-gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
+gcry_err_code_t
+_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);
}
* 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;
}
wipememory (sexp->d, p - sexp->d);
}
- gcry_free ( sexp );
+ xfree ( 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;
* with a NULL.
*/
gcry_sexp_t
-gcry_sexp_alist( const gcry_sexp_t *array )
+_gcry_sexp_alist( const gcry_sexp_t *array )
{
(void)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
* 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;
}
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;
* 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;
}
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
* 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;
- int type;
- int length = 0;
- int level = 0;
-
- if ( !list )
- return 0;
-
- p = list->d;
- while ( (type=*p) != ST_STOP ) {
- p++;
- if ( type == ST_DATA ) {
- memcpy ( &n, p, sizeof n );
- p += sizeof n + n;
- if ( level == 1 )
- length++;
+ const byte *p;
+ DATALEN n;
+ int type;
+ int length = 0;
+ int level = 0;
+
+ if (!list)
+ return 0;
+
+ p = list->d;
+ while ((type=*p) != ST_STOP)
+ {
+ p++;
+ if (type == ST_DATA)
+ {
+ memcpy (&n, p, sizeof n);
+ p += sizeof n + n;
+ if (level == 1)
+ length++;
}
- else if ( type == ST_OPEN ) {
- if ( level == 1 )
- length++;
- level++;
+ else if (type == ST_OPEN)
+ {
+ if (level == 1)
+ length++;
+ level++;
}
- else if ( type == ST_CLOSE ) {
- level--;
+ else if (type == ST_CLOSE)
+ {
+ level--;
}
}
- return length;
+ return length;
}
/* 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)
-/* 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;
- gcry_sexp_t newlist;
- byte *d;
- int level = 0;
-
- if ( !list || list->d[0] != ST_OPEN )
- return NULL;
- p = list->d;
-
- while ( number > 0 ) {
- p++;
- if ( *p == ST_DATA ) {
- memcpy ( &n, ++p, sizeof n );
- p += sizeof n + n;
- p--;
- if ( !level )
- number--;
+ const byte *p;
+ DATALEN n;
+ gcry_sexp_t newlist;
+ byte *d;
+ int level = 0;
+
+ if (!list || list->d[0] != ST_OPEN)
+ return NULL;
+ p = list->d;
+
+ while (number > 0)
+ {
+ p++;
+ if (*p == ST_DATA)
+ {
+ memcpy (&n, ++p, sizeof n);
+ p += sizeof n + n;
+ p--;
+ if (!level)
+ number--;
}
- else if ( *p == ST_OPEN ) {
- level++;
+ else if (*p == ST_OPEN)
+ {
+ level++;
}
- else if ( *p == ST_CLOSE ) {
- level--;
- if ( !level )
- number--;
+ else if (*p == ST_CLOSE)
+ {
+ level--;
+ if ( !level )
+ number--;
}
- else if ( *p == ST_STOP ) {
- return NULL;
+ else if (*p == ST_STOP)
+ {
+ return NULL;
}
}
- p++;
+ p++;
- if ( *p == ST_DATA ) {
- memcpy ( &n, p, sizeof n ); p += sizeof n;
- newlist = gcry_malloc ( sizeof *newlist + n + 1 );
- if (!newlist)
- return NULL;
- d = newlist->d;
- memcpy ( d, p, n ); d += n;
- *d++ = ST_STOP;
+ if (*p == ST_DATA)
+ {
+ memcpy (&n, p+1, sizeof n);
+ newlist = xtrymalloc (sizeof *newlist + 1 + 1 + sizeof n + n + 1);
+ if (!newlist)
+ return NULL;
+ d = newlist->d;
+ *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 ) {
- const byte *head = p;
-
- level = 1;
- do {
- p++;
- if ( *p == ST_DATA ) {
- memcpy ( &n, ++p, sizeof n );
- p += sizeof n + n;
- p--;
- }
- else if ( *p == ST_OPEN ) {
- level++;
- }
- else if ( *p == ST_CLOSE ) {
- level--;
- }
- else if ( *p == ST_STOP ) {
- BUG ();
- }
- } while ( level );
- n = p + 1 - head;
+ else if (*p == ST_OPEN)
+ {
+ const byte *head = p;
- newlist = gcry_malloc ( sizeof *newlist + n );
- if (!newlist)
- return NULL;
- d = newlist->d;
- memcpy ( d, head, n ); d += n;
- *d++ = ST_STOP;
+ level = 1;
+ do {
+ p++;
+ if (*p == ST_DATA)
+ {
+ memcpy (&n, ++p, sizeof n);
+ p += sizeof n + n;
+ p--;
+ }
+ else if (*p == ST_OPEN)
+ {
+ level++;
+ }
+ else if (*p == ST_CLOSE)
+ {
+ level--;
+ }
+ else if (*p == ST_STOP)
+ {
+ BUG ();
+ }
+ } while (level);
+ n = p + 1 - head;
+
+ newlist = xtrymalloc (sizeof *newlist + n);
+ if (!newlist)
+ return NULL;
+ d = newlist->d;
+ memcpy (d, head, n);
+ d += n;
+ *d++ = ST_STOP;
}
- else
- newlist = NULL;
+ else
+ newlist = NULL;
- return normalize (newlist);
+ 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;
return NULL; /* Not a list but N > 0 requested. */
/* Skip over N elements. */
- while ( number > 0 )
+ while (number > 0)
{
- if ( *p == ST_DATA )
+ if (*p == ST_DATA)
{
memcpy ( &n, ++p, sizeof n );
p += sizeof n + n;
if ( !level )
number--;
}
- else if ( *p == ST_OPEN )
+ else if (*p == ST_OPEN)
{
level++;
}
- else if ( *p == ST_CLOSE )
+ else if (*p == ST_CLOSE)
{
level--;
if ( !level )
number--;
}
- else if ( *p == ST_STOP )
+ else if (*p == ST_STOP)
{
return NULL;
}
}
/* If this is data, return it. */
- if ( *p == ST_DATA )
+ if (*p == ST_DATA)
{
memcpy ( &n, ++p, sizeof n );
*datalen = n;
/* 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);
+}
+
+
+/* Get the nth element of a list which needs to be a simple object.
+ The returned value is a malloced buffer and needs to be freed by
+ 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)
+{
+ const char *s;
+ size_t n;
+ char *buf;
+
+ *rlength = 0;
+ s = do_sexp_nth_data (list, number, &n);
+ if (!s || !n)
+ return NULL;
+ buf = xtrymalloc (n);
+ if (!buf)
+ return NULL;
+ memcpy (buf, s, n);
+ *rlength = n;
+ return buf;
}
/* 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);
return buf;
}
+
/*
* 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;
+ if (mpifmt == GCRYMPI_FMT_OPAQUE)
+ {
+ char *p;
- s = sexp_nth_data (list, number, &n);
- if (!s)
- return NULL;
+ p = _gcry_sexp_nth_buffer (list, number, &n);
+ if (!p)
+ return NULL;
- if ( gcry_mpi_scan ( &a, mpifmt, s, n, NULL ) )
- return NULL;
+ 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;
+
+ s = do_sexp_nth_data (list, number, &n);
+ if (!s)
+ return NULL;
+
+ if (_gcry_mpi_scan (&a, mpifmt, s, n, NULL))
+ return NULL;
+ }
return a;
}
* 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;
- DATALEN n;
- gcry_sexp_t newlist;
- byte *d;
- int level = 0;
- int skip = 1;
-
- if ( !list || list->d[0] != ST_OPEN )
- return NULL;
- p = list->d;
-
- while ( skip > 0 ) {
- p++;
- if ( *p == ST_DATA ) {
- memcpy ( &n, ++p, sizeof n );
- p += sizeof n + n;
- p--;
- if ( !level )
- skip--;
+ const byte *p;
+ const byte *head;
+ DATALEN n;
+ gcry_sexp_t newlist;
+ byte *d;
+ int level = 0;
+ int skip = 1;
+
+ if (!list || list->d[0] != ST_OPEN)
+ return NULL;
+ p = list->d;
+
+ while (skip > 0)
+ {
+ p++;
+ if (*p == ST_DATA)
+ {
+ memcpy ( &n, ++p, sizeof n );
+ p += sizeof n + n;
+ p--;
+ if ( !level )
+ skip--;
}
- else if ( *p == ST_OPEN ) {
- level++;
+ else if (*p == ST_OPEN)
+ {
+ level++;
}
- else if ( *p == ST_CLOSE ) {
- level--;
- if ( !level )
- skip--;
+ else if (*p == ST_CLOSE)
+ {
+ level--;
+ if ( !level )
+ skip--;
}
- else if ( *p == ST_STOP ) {
- return NULL;
+ else if (*p == ST_STOP)
+ {
+ return NULL;
}
}
+ p++;
+
+ head = p;
+ level = 0;
+ do {
+ if (*p == ST_DATA)
+ {
+ memcpy ( &n, ++p, sizeof n );
+ p += sizeof n + n;
+ p--;
+ }
+ else if (*p == ST_OPEN)
+ {
+ level++;
+ }
+ else if (*p == ST_CLOSE)
+ {
+ level--;
+ }
+ else if (*p == ST_STOP)
+ {
+ return NULL;
+ }
p++;
+ } while (level);
+ n = p - head;
- head = p;
- level = 0;
- do {
- if ( *p == ST_DATA ) {
- memcpy ( &n, ++p, sizeof n );
- p += sizeof n + n;
- p--;
- }
- else if ( *p == ST_OPEN ) {
- level++;
- }
- else if ( *p == ST_CLOSE ) {
- level--;
- }
- else if ( *p == ST_STOP ) {
- return NULL;
- }
- p++;
- } while ( level );
- n = p - head;
-
- newlist = gcry_malloc ( sizeof *newlist + n + 2 );
- if (!newlist)
- return NULL;
- d = newlist->d;
- *d++ = ST_OPEN;
- memcpy ( d, head, n ); d += n;
- *d++ = ST_CLOSE;
- *d++ = ST_STOP;
-
- return normalize (newlist);
+ newlist = xtrymalloc (sizeof *newlist + n + 2);
+ if (!newlist)
+ return NULL;
+ d = newlist->d;
+ *d++ = ST_OPEN;
+ memcpy (d, head, n);
+ d += n;
+ *d++ = ST_CLOSE;
+ *d++ = ST_STOP;
+
+ return normalize (newlist);
}
+
gcry_sexp_t
-gcry_sexp_cadr ( const gcry_sexp_t list )
+_gcry_sexp_cadr ( const gcry_sexp_t list )
{
- gcry_sexp_t a, b;
+ gcry_sexp_t a, b;
- a = gcry_sexp_cdr ( list );
- b = gcry_sexp_car ( a );
- gcry_sexp_release ( a );
- return b;
+ a = _gcry_sexp_cdr (list);
+ b = _gcry_sexp_car (a);
+ sexp_release (a);
+ return b;
}
-
-static int
-hextobyte( const byte *s )
+static GPG_ERR_INLINE int
+hextonibble (int s)
{
- int c=0;
-
- if( *s >= '0' && *s <= '9' )
- c = 16 * (*s - '0');
- else if( *s >= 'A' && *s <= 'F' )
- c = 16 * (10 + *s - 'A');
- else if( *s >= 'a' && *s <= 'f' ) {
- c = 16 * (10 + *s - 'a');
- }
- s++;
- if( *s >= '0' && *s <= '9' )
- c += *s - '0';
- else if( *s >= 'A' && *s <= 'F' )
- c += 10 + *s - 'A';
- else if( *s >= 'a' && *s <= 'f' ) {
- c += 10 + *s - 'a';
- }
- return c;
+ if (s >= '0' && s <= '9')
+ return s - '0';
+ else if (s >= 'A' && s <= 'F')
+ return 10 + s - 'A';
+ else if (s >= 'a' && s <= 'f')
+ return 10 + s - 'a';
+ else
+ return 0;
}
-struct make_space_ctx {
- gcry_sexp_t sexp;
- size_t allocated;
- byte *pos;
+
+struct make_space_ctx
+{
+ gcry_sexp_t sexp;
+ size_t allocated;
+ byte *pos;
};
+
static gpg_err_code_t
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;
* common operation gcry_sexp_cdr_mpi() will always return a secure MPI
* regardless whether it is needed or not.
*/
-static gcry_error_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)
+static gpg_err_code_t
+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[] =
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;
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);
STORE_LEN (c.pos, datalen);
for (hexfmt++; hexfmt < p; hexfmt++)
{
+ int tmpc;
+
if (whitespacep (hexfmt))
continue;
- *c.pos++ = hextobyte ((const unsigned char*)hexfmt);
- hexfmt++;
+ tmpc = hextonibble (*(const unsigned char*)hexfmt);
+ for (hexfmt++; hexfmt < p && whitespacep (hexfmt); hexfmt++)
+ ;
+ if (hexfmt < p)
+ {
+ tmpc *= 16;
+ tmpc += hextonibble (*(const unsigned char*)hexfmt);
+ }
+ *c.pos++ = tmpc;
}
hexfmt = NULL;
}
}
else if (percent)
{
- if (*p == 'm')
+ if (*p == 'm' || *p == 'M')
{
/* Insert an MPI. */
gcry_mpi_t m;
size_t nm = 0;
+ int mpifmt = *p == 'm'? GCRYMPI_FMT_STD: GCRYMPI_FMT_USG;
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);
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;
}
}
else
{
- if (gcry_mpi_print (GCRYMPI_FMT_STD, 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);
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 (GCRYMPI_FMT_STD, c.pos, nm, &nm, m))
+ if (_gcry_mpi_print (mpifmt, c.pos, nm, &nm, m))
BUG ();
c.pos += nm;
}
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);
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;
}
/* Insert an integer as string. */
int aint;
size_t alen;
- char buf[20];
+ char buf[35];
ARG_NEXT (aint, int);
sprintf (buf, "%d", aint);
memcpy (c.pos, buf, alen);
c.pos += alen;
}
+ else if (*p == 'u')
+ {
+ /* Insert an unsigned integer as string. */
+ unsigned int aint;
+ size_t alen;
+ char buf[35];
+
+ ARG_NEXT (aint, unsigned int);
+ sprintf (buf, "%u", aint);
+ alen = strlen (buf);
+ MAKE_SPACE (alen);
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, alen);
+ memcpy (c.pos, buf, alen);
+ c.pos += alen;
+ }
else if (*p == 'S')
{
/* Insert a gcry_sexp_t. */
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);
- return gcry_error (err);
+ return err;
#undef MAKE_SPACE
#undef STORE_LEN
}
-static gcry_error_t
-sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
- const char *buffer, size_t length, int argflag,
- void **arg_list, ...)
+static gpg_err_code_t
+do_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+ const char *buffer, size_t length, int argflag,
+ void **arg_list, ...)
{
- gcry_error_t rc;
+ 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;
}
-gcry_error_t
-gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
+gpg_err_code_t
+_gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
{
- gcry_error_t rc;
+ 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;
}
-gcry_error_t
+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_error_t
-gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
- const char *format, void **arg_list)
+gcry_err_code_t
+_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_error_t
-gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
- const char *buffer, size_t length)
+gcry_err_code_t
+_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
if (!length)
return 1;
+ if (*buffer & 0x80)
+ return 0; /* If the MSB is set we assume that buffer represents a
+ negative number. */
+ if (!*buffer)
+ return 0; /* Starting with a zero is pretty much a binary string. */
+
for (s=buffer; length; s++, length--)
{
if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))
* 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;
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_error_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;
unsigned int datalen = 0;
size_t dummy_erroff;
- gcry_error_t dummy_errcode;
+ gcry_err_code_t dummy_errcode;
size_t count = 0;
int level = 0;
if (!errcode)
errcode = &dummy_errcode;
- *errcode = gcry_error (GPG_ERR_NO_ERROR);
+ *errcode = GPG_ERR_NO_ERROR;
*erroff = 0;
if (!buffer)
return 0;
if (*buffer != '(')
{
- *errcode = gcry_error (GPG_ERR_SEXP_NOT_CANONICAL);
+ *errcode = GPG_ERR_SEXP_NOT_CANONICAL;
return 0;
}
if (length && count >= length)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+ *errcode = GPG_ERR_SEXP_STRING_TOO_LONG;
return 0;
}
if (length && (count+datalen) >= length)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+ *errcode = GPG_ERR_SEXP_STRING_TOO_LONG;
return 0;
}
count += datalen;
else
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_INV_LEN_SPEC);
+ *errcode = GPG_ERR_SEXP_INV_LEN_SPEC;
return 0;
}
}
if (disphint)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ *errcode = GPG_ERR_SEXP_UNMATCHED_DH;
return 0;
}
level++;
if (!level)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_PAREN);
+ *errcode = GPG_ERR_SEXP_UNMATCHED_PAREN;
return 0;
}
if (disphint)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ *errcode = GPG_ERR_SEXP_UNMATCHED_DH;
return 0;
}
if (!--level)
if (disphint)
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_NESTED_DH);
+ *errcode = GPG_ERR_SEXP_NESTED_DH;
return 0;
}
disphint = p;
if ( !disphint )
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ *errcode = GPG_ERR_SEXP_UNMATCHED_DH;
return 0;
}
disphint = NULL;
if (*p == '0')
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_ZERO_PREFIX);
+ *errcode = GPG_ERR_SEXP_ZERO_PREFIX;
return 0;
}
datalen = atoi_1 (p);
else if (*p == '&' || *p == '\\')
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
+ *errcode = GPG_ERR_SEXP_UNEXPECTED_PUNC;
return 0;
}
else
{
*erroff = count;
- *errcode = gcry_error (GPG_ERR_SEXP_BAD_CHARACTER);
+ *errcode = GPG_ERR_SEXP_BAD_CHARACTER;
return 0;
}
}
}
+
+
+/* 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;
+}