diff options
Diffstat (limited to 'libgcrypt-1.4.6/src/sexp.c')
-rw-r--r-- | libgcrypt-1.4.6/src/sexp.c | 3950 |
1 files changed, 1976 insertions, 1974 deletions
diff --git a/libgcrypt-1.4.6/src/sexp.c b/libgcrypt-1.4.6/src/sexp.c index 99d0141..e11f86e 100644 --- a/libgcrypt-1.4.6/src/sexp.c +++ b/libgcrypt-1.4.6/src/sexp.c @@ -1,1974 +1,1976 @@ -/* sexp.c - S-Expression handling - * Copyright (C) 1999, 2000, 2001, 2002, 2003, - * 2004, 2006, 2007, 2008 Free Software Foundation, Inc. - * - * This file is part of Libgcrypt. - * - * Libgcrypt is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser general Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * Libgcrypt is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <ctype.h> -#include <errno.h> - -#define GCRYPT_NO_MPI_MACROS 1 -#include "g10lib.h" -#include "memory.h" - -typedef struct gcry_sexp *NODE; -typedef unsigned short DATALEN; - -struct gcry_sexp -{ - byte d[1]; -}; - -#define ST_STOP 0 -#define ST_DATA 1 /* datalen follows */ -#define ST_HINT 2 /* datalen follows */ -#define ST_OPEN 3 -#define ST_CLOSE 4 - -/* 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 xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) - -#define TOKEN_SPECIALS "-./_:*+=" - -static gcry_error_t -sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, - const char *buffer, size_t length, int argflag, - va_list arg_ptr, void **arg_list); - -/* Return true if P points to a byte containing a whitespace according - to the S-expressions definition. */ -#undef whitespacep -static GPG_ERR_INLINE int -whitespacep (const char *p) -{ - switch (*p) - { - case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1; - default: return 0; - } -} - - -#if 0 -static void -dump_mpi( gcry_mpi_t a ) -{ - char buffer[1000]; - size_t n = 1000; - - if( !a ) - fputs("[no MPI]", stderr ); - else if( gcry_mpi_print( GCRYMPI_FMT_HEX, buffer, &n, a ) ) - fputs("[MPI too large to print]", stderr ); - else - fputs( buffer, stderr ); -} -#endif - -static void -dump_string (const byte *p, size_t n, int delim ) -{ - for (; n; n--, p++ ) - { - if ((*p & 0x80) || iscntrl( *p ) || *p == delim ) - { - if( *p == '\n' ) - log_printf ("\\n"); - else if( *p == '\r' ) - log_printf ("\\r"); - else if( *p == '\f' ) - log_printf ("\\f"); - else if( *p == '\v' ) - log_printf ("\\v"); - else if( *p == '\b' ) - log_printf ("\\b"); - else if( !*p ) - log_printf ("\\0"); - else - log_printf ("\\x%02x", *p ); - } - else - log_printf ("%c", *p); - } -} - - -void -gcry_sexp_dump (const gcry_sexp_t a) -{ - const byte *p; - int indent = 0; - int type; - - if (!a) - { - log_printf ( "[nil]\n"); - return; - } - - p = a->d; - while ( (type = *p) != ST_STOP ) - { - p++; - switch ( type ) - { - case ST_OPEN: - log_printf ("%*s[open]\n", 2*indent, ""); - indent++; - break; - case ST_CLOSE: - if( indent ) - indent--; - log_printf ("%*s[close]\n", 2*indent, ""); - break; - case ST_DATA: { - DATALEN n; - memcpy ( &n, p, sizeof n ); - p += sizeof n; - log_printf ("%*s[data=\"", 2*indent, "" ); - dump_string (p, n, '\"' ); - log_printf ("\"]\n"); - p += n; - } - break; - default: - log_printf ("%*s[unknown tag %d]\n", 2*indent, "", type); - break; - } - } -} - -/**************** - * Pass list through except when it is an empty list - in that case - * return NULL and release the passed list. - */ -static gcry_sexp_t -normalize ( gcry_sexp_t list ) -{ - unsigned char *p; - - if ( !list ) - return NULL; - p = list->d; - if ( *p == ST_STOP ) - { - /* this is "" */ - gcry_sexp_release ( list ); - return NULL; - } - if ( *p == ST_OPEN && p[1] == ST_CLOSE ) - { - /* this is "()" */ - gcry_sexp_release ( list ); - return NULL; - } - - return list; -} -#pragma runtime_checks("su", off) -/* Create a new S-expression object by reading LENGTH bytes from - BUFFER, assuming it is canonical encoded or autodetected encoding - when AUTODETECT is set to 1. With FREEFNC not NULL, ownership of - the buffer is transferred to the newly created object. FREEFNC - should be the freefnc used to release BUFFER; there is no guarantee - at which point this function is called; most likey you want to use - free() or gcry_free(). - - Passing LENGTH and AUTODETECT as 0 is allowed to indicate that - BUFFER points to a valid canonical encoded S-expression. A LENGTH - of 0 and AUTODETECT 1 indicates that buffer points to a - null-terminated string. - - 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, - int autodetect, void (*freefnc)(void*) ) -{ - gcry_error_t errcode; - gcry_sexp_t se; - volatile va_list dummy_arg_ptr; - - if (!retsexp) - return gcry_error (GPG_ERR_INV_ARG); - *retsexp = NULL; - if (autodetect < 0 || autodetect > 1 || !buffer) - return gcry_error (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); - if (!length) - return errcode; - } - else if (!length && autodetect) - { /* buffer is a string */ - length = strlen ((char *)buffer); - } - - errcode = sexp_sscan (&se, NULL, buffer, length, 0, dummy_arg_ptr, NULL); - if (errcode) - return errcode; - - *retsexp = se; - if (freefnc) - { - /* For now we release the buffer immediately. As soon as we - have changed the internal represenation of S-expression to - the canoncial format - which has the advantage of faster - parsing - we will use this function as a closure in our - GCRYSEXP object and use the BUFFER directly. */ - freefnc (buffer); - } - return gcry_error (GPG_ERR_NO_ERROR); -} - -/* 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, - int autodetect) -{ - 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 ) -{ - if (sexp) - { - if (gcry_is_secure (sexp)) - { - /* Extra paranoid wiping. */ - const byte *p = sexp->d; - int type; - - while ( (type = *p) != ST_STOP ) - { - p++; - switch ( type ) - { - case ST_OPEN: - break; - case ST_CLOSE: - break; - case ST_DATA: - { - DATALEN n; - memcpy ( &n, p, sizeof n ); - p += sizeof n; - p += n; - } - break; - default: - break; - } - } - wipememory (sexp->d, p - sexp->d); - } - gcry_free ( sexp ); - } -} - - -/**************** - * Make a pair from lists a and b, don't use a or b later on. - * Special behaviour: If one is a single element list we put the - * element straight into the new pair. - */ -gcry_sexp_t -gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b ) -{ - (void)a; - (void)b; - - /* NYI: Implementation should be quite easy with our new data - representation */ - BUG (); - return NULL; -} - - -/**************** - * Make a list from all items in the array the end of the array is marked - * with a NULL. - */ -gcry_sexp_t -gcry_sexp_alist( const gcry_sexp_t *array ) -{ - (void)array; - - /* NYI: Implementation should be quite easy with our new data - representation. */ - BUG (); - return NULL; -} - -/**************** - * 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, ... ) -{ - (void)a; - /* NYI: Implementation should be quite easy with our new data - representation. */ - BUG (); - return NULL; -} - - -/**************** - * Append n to the list a - * Returns: a new ist (which maybe a) - */ -gcry_sexp_t -gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n ) -{ - (void)a; - (void)n; - /* NYI: Implementation should be quite easy with our new data - representation. */ - BUG (); - return NULL; -} - -gcry_sexp_t -gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n ) -{ - (void)a; - (void)n; - /* NYI: Implementation should be quite easy with our new data - representation. */ - BUG (); - return NULL; -} - - - -/**************** - * Locate token in a list. The token must be the car of a sublist. - * 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 ) -{ - const byte *p; - DATALEN n; - - if ( !list ) - return NULL; - - if ( !toklen ) - toklen = strlen(tok); - - p = list->d; - while ( *p != ST_STOP ) - { - if ( *p == ST_OPEN && p[1] == ST_DATA ) - { - const byte *head = p; - - p += 2; - memcpy ( &n, p, sizeof n ); - p += sizeof n; - if ( n == toklen && !memcmp( p, tok, toklen ) ) - { /* found it */ - gcry_sexp_t newlist; - byte *d; - int level = 1; - - /* Look for the end of the list. */ - for ( p += n; level; p++ ) - { - if ( *p == ST_DATA ) - { - memcpy ( &n, ++p, sizeof n ); - p += sizeof n + n; - p--; /* Compensate for later increment. */ - } - else if ( *p == ST_OPEN ) - { - level++; - } - else if ( *p == ST_CLOSE ) - { - level--; - } - else if ( *p == ST_STOP ) - { - BUG (); - } - } - n = p - head; - - newlist = gcry_malloc ( sizeof *newlist + n ); - if (!newlist) - { - /* No way to return an error code, so we can only - return Not Found. */ - return NULL; - } - d = newlist->d; - memcpy ( d, head, n ); d += n; - *d++ = ST_STOP; - return normalize ( newlist ); - } - p += n; - } - else if ( *p == ST_DATA ) - { - memcpy ( &n, ++p, sizeof n ); p += sizeof n; - p += n; - } - else - p++; - } - return NULL; -} - -/**************** - * Return the length of the given list - */ -int -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++; - } - else if ( type == ST_OPEN ) { - if ( level == 1 ) - length++; - level++; - } - else if ( type == ST_CLOSE ) { - level--; - } - } - 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 corresponding ST_CLOSE inclusive. */ -static size_t -get_internal_buffer (const gcry_sexp_t list, size_t *r_off) -{ - const unsigned char *p; - DATALEN n; - int type; - int level = 0; - - *r_off = 0; - if (list) - { - p = list->d; - while ( (type=*p) != ST_STOP ) - { - p++; - if (type == ST_DATA) - { - memcpy (&n, p, sizeof n); - p += sizeof n + n; - } - else if (type == ST_OPEN) - { - if (!level) - *r_off = (p-1) - list->d; - level++; - } - else if ( type == ST_CLOSE ) - { - level--; - if (!level) - return p - list->d; - } - } - } - return 0; /* Not a proper list. */ -} - - - -/* Extract the CAR of the given list. May return NULL for bad lists - or memory failure. */ -gcry_sexp_t -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--; - } - else if ( *p == ST_OPEN ) { - level++; - } - else if ( *p == ST_CLOSE ) { - level--; - if ( !level ) - number--; - } - else if ( *p == ST_STOP ) { - return NULL; - } - } - 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; - } - 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; - - newlist = gcry_malloc ( sizeof *newlist + n ); - if (!newlist) - return NULL; - d = newlist->d; - memcpy ( d, head, n ); d += n; - *d++ = ST_STOP; - } - else - newlist = NULL; - - return normalize (newlist); -} - -gcry_sexp_t -gcry_sexp_car( const gcry_sexp_t list ) -{ - 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) -{ - const byte *p; - DATALEN n; - int level = 0; - - *datalen = 0; - if ( !list ) - return NULL; - - p = list->d; - if ( *p == ST_OPEN ) - p++; /* Yep, a list. */ - else if (number) - return NULL; /* Not a list but N > 0 requested. */ - - /* Skip over N elements. */ - while ( number > 0 ) - { - 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_CLOSE ) - { - level--; - if ( !level ) - number--; - } - else if ( *p == ST_STOP ) - { - return NULL; - } - p++; - } - - /* If this is data, return it. */ - if ( *p == ST_DATA ) - { - memcpy ( &n, ++p, sizeof n ); - *datalen = n; - return (const char*)p + sizeof n; - } - - return NULL; -} - - -/* 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 ) -{ - return sexp_nth_data (list, number, datalen); -} - - -/* 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) -{ - const char *s; - size_t n; - char *buf; - - s = sexp_nth_data (list, number, &n); - if (!s || n < 1 || (n+1) < 1) - return NULL; - buf = gcry_malloc (n+1); - if (!buf) - return NULL; - memcpy (buf, s, n); - buf[n] = 0; - return buf; -} - -/* - * Get a MPI from the car - */ -gcry_mpi_t -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 ( 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 ) -{ - 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_CLOSE ) { - level--; - if ( !level ) - skip--; - } - 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; - - 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); -} - -gcry_sexp_t -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 ); - return b; -} - - - -static int -hextobyte( const byte *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; -} - -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 ) -{ - size_t used = c->pos - c->sexp->d; - - if ( used + n + sizeof(DATALEN) + 1 >= c->allocated ) - { - gcry_sexp_t newsexp; - byte *newhead; - size_t newsize; - - 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); - if (!newsexp) - return gpg_err_code_from_errno (errno); - c->allocated = newsize; - newhead = newsexp->d; - c->pos = newhead + used; - c->sexp = newsexp; - } - return 0; -} - - -/* Unquote STRING of LENGTH and store it into BUF. The surrounding - quotes are must already be removed from STRING. We assume that the - quoted string is syntacillay correct. */ -static size_t -unquote_string (const char *string, size_t length, unsigned char *buf) -{ - int esc = 0; - const unsigned char *s = (const unsigned char*)string; - unsigned char *d = buf; - size_t n = length; - - for (; n; n--, s++) - { - if (esc) - { - switch (*s) - { - case 'b': *d++ = '\b'; break; - case 't': *d++ = '\t'; break; - case 'v': *d++ = '\v'; break; - case 'n': *d++ = '\n'; break; - case 'f': *d++ = '\f'; break; - case 'r': *d++ = '\r'; break; - case '"': *d++ = '\"'; break; - case '\'': *d++ = '\''; break; - case '\\': *d++ = '\\'; break; - - case '\r': /* ignore CR[,LF] */ - if (n>1 && s[1] == '\n') - { - s++; n--; - } - esc = 0; - break; - - case '\n': /* ignore LF[,CR] */ - if (n>1 && s[1] == '\r') - { - s++; n--; - } - break; - - case 'x': /* hex value */ - if (n>2 && hexdigitp (s+1) && hexdigitp (s+2)) - { - s++; n--; - *d++ = xtoi_2 (s); - s++; n--; - } - break; - - default: - if (n>2 && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2)) - { - *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2); - s += 2; - n -= 2; - } - break; - } - esc = 0; - } - else if( *s == '\\' ) - esc = 1; - else - *d++ = *s; - } - - return d - buf; -} - -/**************** - * Scan the provided buffer and return the S expression in our internal - * format. Returns a newly allocated expression. If erroff is not NULL and - * a parsing error has occured, the offset into buffer will be returned. - * If ARGFLAG is true, the function supports some printf like - * expressions. - * These are: - * %m - MPI - * %s - string (no autoswitch to secure allocation) - * %d - integer stored as string (no autoswitch to secure allocation) - * %b - memory buffer; this takes _two_ arguments: an integer with the - * length of the buffer and a pointer to the buffer. - * %S - Copy an gcry_sexp_t here. The S-expression needs to be a - * regular one, starting with a parenthesis. - * (no autoswitch to secure allocation) - * all other format elements are currently not defined and return an error. - * this includes the "%%" sequence becauce the percent sign is not an - * allowed character. - * FIXME: We should find a way to store the secure-MPIs not in the string - * but as reference to somewhere - this can help us to save huge amounts - * of secure memory. The problem is, that if only one element is secure, all - * other elements are automagicaly copied to secure memory too, so the most - * common operation gcry_sexp_cdr_mpi() will always return a secure MPI - * regardless whether it is needed or not. - */ -static gcry_error_t -sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, - const char *buffer, size_t length, int argflag, - va_list arg_ptr, void **arg_list) -{ - gcry_err_code_t err = 0; - static const char tokenchars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789-./_:*+="; - const char *p; - size_t n; - const char *digptr = NULL; - const char *quoted = NULL; - const char *tokenp = NULL; - const char *hexfmt = NULL; - const char *base64 = NULL; - const char *disphint = NULL; - const char *percent = NULL; - int hexcount = 0; - int quoted_esc = 0; - int datalen = 0; - size_t dummy_erroff; - struct make_space_ctx c; - int arg_counter = 0; - int level = 0; - - if (!erroff) - erroff = &dummy_erroff; - - /* Depending on wether ARG_LIST is non-zero or not, this macro gives - us the next argument, either from the variable argument list as - specified by ARG_PTR or from the argument array ARG_LIST. */ -#define ARG_NEXT(storage, type) \ - do \ - { \ - if (!arg_list) \ - storage = va_arg (arg_ptr, type); \ - else \ - storage = *((type *) (arg_list[arg_counter++])); \ - } \ - while (0) - - /* The MAKE_SPACE macro is used before each store operation to - ensure that the buffer is large enough. It requires a global - context named C and jumps out to the label LEAVE on error! It - also sets ERROFF using the variables BUFFER and P. */ -#define MAKE_SPACE(n) do { \ - gpg_err_code_t _ms_err = make_space (&c, (n)); \ - if (_ms_err) \ - { \ - err = _ms_err; \ - *erroff = p - buffer; \ - goto leave; \ - } \ - } while (0) - - /* The STORE_LEN macro is used to store the length N at buffer P. */ -#define STORE_LEN(p,n) do { \ - DATALEN ashort = (n); \ - memcpy ( (p), &ashort, sizeof(ashort) ); \ - (p) += sizeof (ashort); \ - } while (0) - - /* We assume that the internal representation takes less memory than - 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); - else - c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1); - if (!c.sexp) - { - err = gpg_err_code_from_errno (errno); - *erroff = 0; - goto leave; - } - c.pos = c.sexp->d; - - for (p = buffer, n = length; n; p++, n--) - { - if (tokenp && !hexfmt) - { - if (strchr (tokenchars, *p)) - continue; - else - { - datalen = p - tokenp; - MAKE_SPACE (datalen); - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, datalen); - memcpy (c.pos, tokenp, datalen); - c.pos += datalen; - tokenp = NULL; - } - } - - if (quoted) - { - if (quoted_esc) - { - switch (*p) - { - case 'b': case 't': case 'v': case 'n': case 'f': - case 'r': case '"': case '\'': case '\\': - quoted_esc = 0; - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': - if (!((n > 2) - && (p[1] >= '0') && (p[1] <= '7') - && (p[2] >= '0') && (p[2] <= '7'))) - { - *erroff = p - buffer; - /* Invalid octal value. */ - err = GPG_ERR_SEXP_BAD_QUOTATION; - goto leave; - } - p += 2; - n -= 2; - quoted_esc = 0; - break; - - case 'x': - if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2))) - { - *erroff = p - buffer; - /* Invalid hex value. */ - err = GPG_ERR_SEXP_BAD_QUOTATION; - goto leave; - } - p += 2; - n -= 2; - quoted_esc = 0; - break; - - case '\r': - /* ignore CR[,LF] */ - if (n && (p[1] == '\n')) - { - p++; - n--; - } - quoted_esc = 0; - break; - - case '\n': - /* ignore LF[,CR] */ - if (n && (p[1] == '\r')) - { - p++; - n--; - } - quoted_esc = 0; - break; - - default: - *erroff = p - buffer; - /* Invalid quoted string escape. */ - err = GPG_ERR_SEXP_BAD_QUOTATION; - goto leave; - } - } - else if (*p == '\\') - quoted_esc = 1; - else if (*p == '\"') - { - /* Keep it easy - we know that the unquoted string will - never be larger. */ - unsigned char *save; - size_t len; - - quoted++; /* Skip leading quote. */ - MAKE_SPACE (p - quoted); - *c.pos++ = ST_DATA; - save = c.pos; - STORE_LEN (c.pos, 0); /* Will be fixed up later. */ - len = unquote_string (quoted, p - quoted, c.pos); - c.pos += len; - STORE_LEN (save, len); - quoted = NULL; - } - } - else if (hexfmt) - { - if (isxdigit (*p)) - hexcount++; - else if (*p == '#') - { - if ((hexcount & 1)) - { - *erroff = p - buffer; - err = GPG_ERR_SEXP_ODD_HEX_NUMBERS; - goto leave; - } - - datalen = hexcount / 2; - MAKE_SPACE (datalen); - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, datalen); - for (hexfmt++; hexfmt < p; hexfmt++) - { - if (whitespacep (hexfmt)) - continue; - *c.pos++ = hextobyte ((const unsigned char*)hexfmt); - hexfmt++; - } - hexfmt = NULL; - } - else if (!whitespacep (p)) - { - *erroff = p - buffer; - err = GPG_ERR_SEXP_BAD_HEX_CHAR; - goto leave; - } - } - else if (base64) - { - if (*p == '|') - base64 = NULL; - } - else if (digptr) - { - if (digitp (p)) - ; - else if (*p == ':') - { - datalen = atoi (digptr); /* FIXME: check for overflow. */ - digptr = NULL; - if (datalen > n - 1) - { - *erroff = p - buffer; - /* Buffer too short. */ - err = GPG_ERR_SEXP_STRING_TOO_LONG; - goto leave; - } - /* Make a new list entry. */ - MAKE_SPACE (datalen); - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, datalen); - memcpy (c.pos, p + 1, datalen); - c.pos += datalen; - n -= datalen; - p += datalen; - } - else if (*p == '\"') - { - digptr = NULL; /* We ignore the optional length. */ - quoted = p; - quoted_esc = 0; - } - else if (*p == '#') - { - digptr = NULL; /* We ignore the optional length. */ - hexfmt = p; - hexcount = 0; - } - else if (*p == '|') - { - digptr = NULL; /* We ignore the optional length. */ - base64 = p; - } - else - { - *erroff = p - buffer; - err = GPG_ERR_SEXP_INV_LEN_SPEC; - goto leave; - } - } - else if (percent) - { - if (*p == 'm') - { - /* Insert an MPI. */ - gcry_mpi_t m; - size_t nm = 0; - - ARG_NEXT (m, gcry_mpi_t); - - if (gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &nm, m)) - BUG (); - - MAKE_SPACE (nm); - if (!gcry_is_secure (c.sexp->d) - && gcry_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); - if (!newsexp) - { - err = gpg_err_code_from_errno (errno); - goto leave; - } - 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); - c.sexp = newsexp; - } - - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, nm); - if (gcry_mpi_print (GCRYMPI_FMT_STD, c.pos, nm, &nm, m)) - BUG (); - c.pos += nm; - } - else if (*p == 's') - { - /* Insert an string. */ - const char *astr; - size_t alen; - - ARG_NEXT (astr, const char *); - alen = strlen (astr); - - MAKE_SPACE (alen); - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, alen); - memcpy (c.pos, astr, alen); - c.pos += alen; - } - else if (*p == 'b') - { - /* Insert a memory buffer. */ - const char *astr; - int alen; - - ARG_NEXT (alen, int); - ARG_NEXT (astr, const char *); - - MAKE_SPACE (alen); - if (alen - && !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); - if (!newsexp) - { - err = gpg_err_code_from_errno (errno); - goto leave; - } - 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); - c.sexp = newsexp; - } - - *c.pos++ = ST_DATA; - STORE_LEN (c.pos, alen); - memcpy (c.pos, astr, alen); - c.pos += alen; - } - else if (*p == 'd') - { - /* Insert an integer as string. */ - int aint; - size_t alen; - char buf[20]; - - ARG_NEXT (aint, int); - sprintf (buf, "%d", 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. */ - gcry_sexp_t asexp; - size_t alen, aoff; - - ARG_NEXT (asexp, gcry_sexp_t); - alen = get_internal_buffer (asexp, &aoff); - if (alen) - { - MAKE_SPACE (alen); - memcpy (c.pos, asexp->d + aoff, alen); - c.pos += alen; - } - } - else - { - *erroff = p - buffer; - /* Invalid format specifier. */ - err = GPG_ERR_SEXP_INV_LEN_SPEC; - goto leave; - } - percent = NULL; - } - else if (*p == '(') - { - if (disphint) - { - *erroff = p - buffer; - /* Open display hint. */ - err = GPG_ERR_SEXP_UNMATCHED_DH; - goto leave; - } - MAKE_SPACE (0); - *c.pos++ = ST_OPEN; - level++; - } - else if (*p == ')') - { - /* Walk up. */ - if (disphint) - { - *erroff = p - buffer; - /* Open display hint. */ - err = GPG_ERR_SEXP_UNMATCHED_DH; - goto leave; - } - MAKE_SPACE (0); - *c.pos++ = ST_CLOSE; - level--; - } - else if (*p == '\"') - { - quoted = p; - quoted_esc = 0; - } - else if (*p == '#') - { - hexfmt = p; - hexcount = 0; - } - else if (*p == '|') - base64 = p; - else if (*p == '[') - { - if (disphint) - { - *erroff = p - buffer; - /* Open display hint. */ - err = GPG_ERR_SEXP_NESTED_DH; - goto leave; - } - disphint = p; - } - else if (*p == ']') - { - if (!disphint) - { - *erroff = p - buffer; - /* Open display hint. */ - err = GPG_ERR_SEXP_UNMATCHED_DH; - goto leave; - } - disphint = NULL; - } - else if (digitp (p)) - { - if (*p == '0') - { - /* A length may not begin with zero. */ - *erroff = p - buffer; - err = GPG_ERR_SEXP_ZERO_PREFIX; - goto leave; - } - digptr = p; - } - else if (strchr (tokenchars, *p)) - tokenp = p; - else if (whitespacep (p)) - ; - else if (*p == '{') - { - /* fixme: handle rescanning: we can do this by saving our - current state and start over at p+1 -- Hmmm. At this - point here we are in a well defined state, so we don't - need to save it. Great. */ - *erroff = p - buffer; - err = GPG_ERR_SEXP_UNEXPECTED_PUNC; - goto leave; - } - else if (strchr ("&\\", *p)) - { - /* Reserved punctuation. */ - *erroff = p - buffer; - err = GPG_ERR_SEXP_UNEXPECTED_PUNC; - goto leave; - } - else if (argflag && (*p == '%')) - percent = p; - else - { - /* Bad or unavailable. */ - *erroff = p - buffer; - err = GPG_ERR_SEXP_BAD_CHARACTER; - goto leave; - } - } - MAKE_SPACE (0); - *c.pos++ = ST_STOP; - - if (level && !err) - err = GPG_ERR_SEXP_UNMATCHED_PAREN; - - leave: - if (err) - { - /* Error -> deallocate. */ - if (c.sexp) - { - /* Extra paranoid wipe on error. */ - if (gcry_is_secure (c.sexp)) - wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1); - gcry_free (c.sexp); - } - /* This might be expected by existing code... */ - *retsexp = NULL; - } - else - *retsexp = normalize (c.sexp); - - return gcry_error (err); -#undef MAKE_SPACE -#undef STORE_LEN -} - -gcry_error_t -gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...) -{ - gcry_error_t rc; - va_list arg_ptr; - - va_start (arg_ptr, format); - rc = sexp_sscan (retsexp, erroff, format, strlen(format), 1, - arg_ptr, NULL); - va_end (arg_ptr); - - return rc; -} - - -gcry_error_t -_gcry_sexp_vbuild (gcry_sexp_t *retsexp, size_t *erroff, - const char *format, va_list arg_ptr) -{ - return sexp_sscan (retsexp, erroff, format, strlen(format), 1, - arg_ptr, NULL); -} - -/* 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) -{ - /* We don't need the va_list because it is controlled by the - following flag, however we have to pass it but can't initialize - it as there is no portable way to do so. volatile is needed to - suppress the compiler warning */ - volatile va_list dummy_arg_ptr; - - gcry_error_t rc; - - rc = sexp_sscan (retsexp, erroff, format, strlen(format), 1, - dummy_arg_ptr, arg_list); - - return rc; -} - -gcry_error_t -gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, - const char *buffer, size_t length) -{ - /* We don't need the va_list because it is controlled by the - following flag, however we have to pass it but can't initialize - it as there is no portable way to do so. volatile is needed to - suppress the compiler warning */ - volatile va_list dummy_arg_ptr; - - return sexp_sscan (retsexp, erroff, buffer, length, 0, - dummy_arg_ptr, NULL); -} - -#pragma runtime_checks("su", restore) - - -/* Figure out a suitable encoding for BUFFER of LENGTH. - Returns: 0 = Binary - 1 = String possible - 2 = Token possible -*/ -static int -suitable_encoding (const unsigned char *buffer, size_t length) -{ - const unsigned char *s; - int maybe_token = 1; - - if (!length) - return 1; - - for (s=buffer; length; s++, length--) - { - if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0)) - && !strchr ("\b\t\v\n\f\r\"\'\\", *s)) - return 0; /*binary*/ - if ( maybe_token - && !alphap (s) && !digitp (s) && !strchr (TOKEN_SPECIALS, *s)) - maybe_token = 0; - } - s = buffer; - if ( maybe_token && !digitp (s) ) - return 2; - return 1; -} - - -static int -convert_to_hex (const unsigned char *src, size_t len, char *dest) -{ - int i; - - if (dest) - { - *dest++ = '#'; - for (i=0; i < len; i++, dest += 2 ) - sprintf (dest, "%02X", src[i]); - *dest++ = '#'; - } - return len*2+2; -} - -static int -convert_to_string (const unsigned char *s, size_t len, char *dest) -{ - if (dest) - { - char *p = dest; - *p++ = '\"'; - for (; len; len--, s++ ) - { - switch (*s) - { - case '\b': *p++ = '\\'; *p++ = 'b'; break; - case '\t': *p++ = '\\'; *p++ = 't'; break; - case '\v': *p++ = '\\'; *p++ = 'v'; break; - case '\n': *p++ = '\\'; *p++ = 'n'; break; - case '\f': *p++ = '\\'; *p++ = 'f'; break; - case '\r': *p++ = '\\'; *p++ = 'r'; break; - case '\"': *p++ = '\\'; *p++ = '\"'; break; - case '\'': *p++ = '\\'; *p++ = '\''; break; - case '\\': *p++ = '\\'; *p++ = '\\'; break; - default: - if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))) - { - sprintf (p, "\\x%02x", *s); - p += 4; - } - else - *p++ = *s; - } - } - *p++ = '\"'; - return p - dest; - } - else - { - int count = 2; - for (; len; len--, s++ ) - { - switch (*s) - { - case '\b': - case '\t': - case '\v': - case '\n': - case '\f': - case '\r': - case '\"': - case '\'': - case '\\': count += 2; break; - default: - if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))) - count += 4; - else - count++; - } - } - return count; - } -} - - - -static int -convert_to_token (const unsigned char *src, size_t len, char *dest) -{ - if (dest) - memcpy (dest, src, len); - return len; -} - - -/**************** - * Print SEXP to buffer using the MODE. Returns the length of the - * SEXP in buffer or 0 if the buffer is too short (We have at least an - * empty list consisting of 2 bytes). If a buffer of NULL is provided, - * the required length is returned. - */ -size_t -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; - char *d; - DATALEN n; - char numbuf[20]; - size_t len = 0; - int i, indent = 0; - - s = list? list->d : empty; - d = buffer; - while ( *s != ST_STOP ) - { - switch ( *s ) - { - case ST_OPEN: - s++; - if ( mode != GCRYSEXP_FMT_CANON ) - { - if (indent) - len++; - len += indent; - } - len++; - if ( buffer ) - { - if ( len >= maxlength ) - return 0; - if ( mode != GCRYSEXP_FMT_CANON ) - { - if (indent) - *d++ = '\n'; - for (i=0; i < indent; i++) - *d++ = ' '; - } - *d++ = '('; - } - indent++; - break; - case ST_CLOSE: - s++; - len++; - if ( buffer ) - { - if ( len >= maxlength ) - return 0; - *d++ = ')'; - } - indent--; - if (*s != ST_OPEN && *s != ST_STOP && mode != GCRYSEXP_FMT_CANON) - { - len++; - len += indent; - if (buffer) - { - if (len >= maxlength) - return 0; - *d++ = '\n'; - for (i=0; i < indent; i++) - *d++ = ' '; - } - } - break; - case ST_DATA: - s++; - memcpy ( &n, s, sizeof n ); s += sizeof n; - if (mode == GCRYSEXP_FMT_ADVANCED) - { - int type; - size_t nn; - - switch ( (type=suitable_encoding (s, n))) - { - case 1: nn = convert_to_string (s, n, NULL); break; - case 2: nn = convert_to_token (s, n, NULL); break; - default: nn = convert_to_hex (s, n, NULL); break; - } - len += nn; - if (buffer) - { - if (len >= maxlength) - return 0; - switch (type) - { - case 1: convert_to_string (s, n, d); break; - case 2: convert_to_token (s, n, d); break; - default: convert_to_hex (s, n, d); break; - } - d += nn; - } - if (s[n] != ST_CLOSE) - { - len++; - if (buffer) - { - if (len >= maxlength) - return 0; - *d++ = ' '; - } - } - } - else - { - sprintf (numbuf, "%u:", (unsigned int)n ); - len += strlen (numbuf) + n; - if ( buffer ) - { - if ( len >= maxlength ) - return 0; - d = stpcpy ( d, numbuf ); - memcpy ( d, s, n ); d += n; - } - } - s += n; - break; - default: - BUG (); - } - } - if ( mode != GCRYSEXP_FMT_CANON ) - { - len++; - if (buffer) - { - if ( len >= maxlength ) - return 0; - *d++ = '\n'; - } - } - if (buffer) - { - if ( len >= maxlength ) - return 0; - *d++ = 0; /* for convenience we make a C string */ - } - else - len++; /* we need one byte more for this */ - - return len; -} - - -/* Scan a cannocial encoded buffer with implicit length values and - return the actual length this S-expression uses. For a valid S-Exp - it should never return 0. If LENGTH is not zero, the maximum - length to scan is given - this can be used for syntax checks of - 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) -{ - const unsigned char *p; - const unsigned char *disphint = NULL; - unsigned int datalen = 0; - size_t dummy_erroff; - gcry_error_t dummy_errcode; - size_t count = 0; - int level = 0; - - if (!erroff) - erroff = &dummy_erroff; - if (!errcode) - errcode = &dummy_errcode; - - *errcode = gcry_error (GPG_ERR_NO_ERROR); - *erroff = 0; - if (!buffer) - return 0; - if (*buffer != '(') - { - *errcode = gcry_error (GPG_ERR_SEXP_NOT_CANONICAL); - return 0; - } - - for (p=buffer; ; p++, count++ ) - { - if (length && count >= length) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG); - return 0; - } - - if (datalen) - { - if (*p == ':') - { - if (length && (count+datalen) >= length) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG); - return 0; - } - count += datalen; - p += datalen; - datalen = 0; - } - else if (digitp(p)) - datalen = datalen*10 + atoi_1(p); - else - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_INV_LEN_SPEC); - return 0; - } - } - else if (*p == '(') - { - if (disphint) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH); - return 0; - } - level++; - } - else if (*p == ')') - { /* walk up */ - if (!level) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_PAREN); - return 0; - } - if (disphint) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH); - return 0; - } - if (!--level) - return ++count; /* ready */ - } - else if (*p == '[') - { - if (disphint) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_NESTED_DH); - return 0; - } - disphint = p; - } - else if (*p == ']') - { - if ( !disphint ) - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH); - return 0; - } - disphint = NULL; - } - else if (digitp (p) ) - { - if (*p == '0') - { - *erroff = count; - *errcode = gcry_error (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); - return 0; - } - else - { - *erroff = count; - *errcode = gcry_error (GPG_ERR_SEXP_BAD_CHARACTER); - return 0; - } - } -} +/* sexp.c - S-Expression handling
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003,
+ * 2004, 2006, 2007, 2008 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#define GCRYPT_NO_MPI_MACROS 1
+#include "g10lib.h"
+
+typedef struct gcry_sexp *NODE;
+typedef unsigned short DATALEN;
+
+struct gcry_sexp
+{
+ byte d[1];
+};
+
+#define ST_STOP 0
+#define ST_DATA 1 /* datalen follows */
+#define ST_HINT 2 /* datalen follows */
+#define ST_OPEN 3
+#define ST_CLOSE 4
+
+/* 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 xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+#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_error_t
+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. */
+#undef whitespacep
+static GPG_ERR_INLINE int
+whitespacep (const char *p)
+{
+ switch (*p)
+ {
+ case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
+ default: return 0;
+ }
+}
+
+
+#if 0
+static void
+dump_mpi( gcry_mpi_t a )
+{
+ char buffer[1000];
+ size_t n = 1000;
+
+ if( !a )
+ fputs("[no MPI]", stderr );
+ else if( gcry_mpi_print( GCRYMPI_FMT_HEX, buffer, &n, a ) )
+ fputs("[MPI too large to print]", stderr );
+ else
+ fputs( buffer, stderr );
+}
+#endif
+
+static void
+dump_string (const byte *p, size_t n, int delim )
+{
+ for (; n; n--, p++ )
+ {
+ if ((*p & 0x80) || iscntrl( *p ) || *p == delim )
+ {
+ if( *p == '\n' )
+ log_printf ("\\n");
+ else if( *p == '\r' )
+ log_printf ("\\r");
+ else if( *p == '\f' )
+ log_printf ("\\f");
+ else if( *p == '\v' )
+ log_printf ("\\v");
+ else if( *p == '\b' )
+ log_printf ("\\b");
+ else if( !*p )
+ log_printf ("\\0");
+ else
+ log_printf ("\\x%02x", *p );
+ }
+ else
+ log_printf ("%c", *p);
+ }
+}
+
+
+void
+gcry_sexp_dump (const gcry_sexp_t a)
+{
+ const byte *p;
+ int indent = 0;
+ int type;
+
+ if (!a)
+ {
+ log_printf ( "[nil]\n");
+ return;
+ }
+
+ p = a->d;
+ while ( (type = *p) != ST_STOP )
+ {
+ p++;
+ switch ( type )
+ {
+ case ST_OPEN:
+ log_printf ("%*s[open]\n", 2*indent, "");
+ indent++;
+ break;
+ case ST_CLOSE:
+ if( indent )
+ indent--;
+ log_printf ("%*s[close]\n", 2*indent, "");
+ break;
+ case ST_DATA: {
+ DATALEN n;
+ memcpy ( &n, p, sizeof n );
+ p += sizeof n;
+ log_printf ("%*s[data=\"", 2*indent, "" );
+ dump_string (p, n, '\"' );
+ log_printf ("\"]\n");
+ p += n;
+ }
+ break;
+ default:
+ log_printf ("%*s[unknown tag %d]\n", 2*indent, "", type);
+ break;
+ }
+ }
+}
+
+/****************
+ * Pass list through except when it is an empty list - in that case
+ * return NULL and release the passed list.
+ */
+static gcry_sexp_t
+normalize ( gcry_sexp_t list )
+{
+ unsigned char *p;
+
+ if ( !list )
+ return NULL;
+ p = list->d;
+ if ( *p == ST_STOP )
+ {
+ /* this is "" */
+ gcry_sexp_release ( list );
+ return NULL;
+ }
+ if ( *p == ST_OPEN && p[1] == ST_CLOSE )
+ {
+ /* this is "()" */
+ gcry_sexp_release ( list );
+ return NULL;
+ }
+
+ return list;
+}
+
+/* Create a new S-expression object by reading LENGTH bytes from
+ BUFFER, assuming it is canonical encoded or autodetected encoding
+ when AUTODETECT is set to 1. With FREEFNC not NULL, ownership of
+ the buffer is transferred to the newly created object. FREEFNC
+ should be the freefnc used to release BUFFER; there is no guarantee
+ at which point this function is called; most likey you want to use
+ free() or gcry_free().
+
+ Passing LENGTH and AUTODETECT as 0 is allowed to indicate that
+ BUFFER points to a valid canonical encoded S-expression. A LENGTH
+ of 0 and AUTODETECT 1 indicates that buffer points to a
+ null-terminated string.
+
+ 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,
+ int autodetect, void (*freefnc)(void*) )
+{
+ gcry_error_t errcode;
+ gcry_sexp_t se;
+
+ if (!retsexp)
+ return gcry_error (GPG_ERR_INV_ARG);
+ *retsexp = NULL;
+ if (autodetect < 0 || autodetect > 1 || !buffer)
+ return gcry_error (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);
+ if (!length)
+ return errcode;
+ }
+ else if (!length && autodetect)
+ { /* buffer is a string */
+ length = strlen ((char *)buffer);
+ }
+
+ errcode = sexp_sscan (&se, NULL, buffer, length, 0, NULL);
+ if (errcode)
+ return errcode;
+
+ *retsexp = se;
+ if (freefnc)
+ {
+ /* For now we release the buffer immediately. As soon as we
+ have changed the internal represenation of S-expression to
+ the canoncial format - which has the advantage of faster
+ parsing - we will use this function as a closure in our
+ GCRYSEXP object and use the BUFFER directly. */
+ freefnc (buffer);
+ }
+ return gcry_error (GPG_ERR_NO_ERROR);
+}
+
+/* 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,
+ int autodetect)
+{
+ 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 )
+{
+ if (sexp)
+ {
+ if (gcry_is_secure (sexp))
+ {
+ /* Extra paranoid wiping. */
+ const byte *p = sexp->d;
+ int type;
+
+ while ( (type = *p) != ST_STOP )
+ {
+ p++;
+ switch ( type )
+ {
+ case ST_OPEN:
+ break;
+ case ST_CLOSE:
+ break;
+ case ST_DATA:
+ {
+ DATALEN n;
+ memcpy ( &n, p, sizeof n );
+ p += sizeof n;
+ p += n;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ wipememory (sexp->d, p - sexp->d);
+ }
+ gcry_free ( sexp );
+ }
+}
+
+
+/****************
+ * Make a pair from lists a and b, don't use a or b later on.
+ * Special behaviour: If one is a single element list we put the
+ * element straight into the new pair.
+ */
+gcry_sexp_t
+gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
+{
+ (void)a;
+ (void)b;
+
+ /* NYI: Implementation should be quite easy with our new data
+ representation */
+ BUG ();
+ return NULL;
+}
+
+
+/****************
+ * Make a list from all items in the array the end of the array is marked
+ * with a NULL.
+ */
+gcry_sexp_t
+gcry_sexp_alist( const gcry_sexp_t *array )
+{
+ (void)array;
+
+ /* NYI: Implementation should be quite easy with our new data
+ representation. */
+ BUG ();
+ return NULL;
+}
+
+/****************
+ * 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, ... )
+{
+ (void)a;
+ /* NYI: Implementation should be quite easy with our new data
+ representation. */
+ BUG ();
+ return NULL;
+}
+
+
+/****************
+ * Append n to the list a
+ * Returns: a new ist (which maybe a)
+ */
+gcry_sexp_t
+gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
+{
+ (void)a;
+ (void)n;
+ /* NYI: Implementation should be quite easy with our new data
+ representation. */
+ BUG ();
+ return NULL;
+}
+
+gcry_sexp_t
+gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
+{
+ (void)a;
+ (void)n;
+ /* NYI: Implementation should be quite easy with our new data
+ representation. */
+ BUG ();
+ return NULL;
+}
+
+
+
+/****************
+ * Locate token in a list. The token must be the car of a sublist.
+ * 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 )
+{
+ const byte *p;
+ DATALEN n;
+
+ if ( !list )
+ return NULL;
+
+ if ( !toklen )
+ toklen = strlen(tok);
+
+ p = list->d;
+ while ( *p != ST_STOP )
+ {
+ if ( *p == ST_OPEN && p[1] == ST_DATA )
+ {
+ const byte *head = p;
+
+ p += 2;
+ memcpy ( &n, p, sizeof n );
+ p += sizeof n;
+ if ( n == toklen && !memcmp( p, tok, toklen ) )
+ { /* found it */
+ gcry_sexp_t newlist;
+ byte *d;
+ int level = 1;
+
+ /* Look for the end of the list. */
+ for ( p += n; level; p++ )
+ {
+ if ( *p == ST_DATA )
+ {
+ memcpy ( &n, ++p, sizeof n );
+ p += sizeof n + n;
+ p--; /* Compensate for later increment. */
+ }
+ else if ( *p == ST_OPEN )
+ {
+ level++;
+ }
+ else if ( *p == ST_CLOSE )
+ {
+ level--;
+ }
+ else if ( *p == ST_STOP )
+ {
+ BUG ();
+ }
+ }
+ n = p - head;
+
+ newlist = gcry_malloc ( sizeof *newlist + n );
+ if (!newlist)
+ {
+ /* No way to return an error code, so we can only
+ return Not Found. */
+ return NULL;
+ }
+ d = newlist->d;
+ memcpy ( d, head, n ); d += n;
+ *d++ = ST_STOP;
+ return normalize ( newlist );
+ }
+ p += n;
+ }
+ else if ( *p == ST_DATA )
+ {
+ memcpy ( &n, ++p, sizeof n ); p += sizeof n;
+ p += n;
+ }
+ else
+ p++;
+ }
+ return NULL;
+}
+
+/****************
+ * Return the length of the given list
+ */
+int
+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++;
+ }
+ else if ( type == ST_OPEN ) {
+ if ( level == 1 )
+ length++;
+ level++;
+ }
+ else if ( type == ST_CLOSE ) {
+ level--;
+ }
+ }
+ 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 corresponding ST_CLOSE inclusive. */
+static size_t
+get_internal_buffer (const gcry_sexp_t list, size_t *r_off)
+{
+ const unsigned char *p;
+ DATALEN n;
+ int type;
+ int level = 0;
+
+ *r_off = 0;
+ if (list)
+ {
+ p = list->d;
+ while ( (type=*p) != ST_STOP )
+ {
+ p++;
+ if (type == ST_DATA)
+ {
+ memcpy (&n, p, sizeof n);
+ p += sizeof n + n;
+ }
+ else if (type == ST_OPEN)
+ {
+ if (!level)
+ *r_off = (p-1) - list->d;
+ level++;
+ }
+ else if ( type == ST_CLOSE )
+ {
+ level--;
+ if (!level)
+ return p - list->d;
+ }
+ }
+ }
+ return 0; /* Not a proper list. */
+}
+
+
+
+/* Extract the CAR of the given list. May return NULL for bad lists
+ or memory failure. */
+gcry_sexp_t
+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--;
+ }
+ else if ( *p == ST_OPEN ) {
+ level++;
+ }
+ else if ( *p == ST_CLOSE ) {
+ level--;
+ if ( !level )
+ number--;
+ }
+ else if ( *p == ST_STOP ) {
+ return NULL;
+ }
+ }
+ 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;
+ }
+ 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;
+
+ newlist = gcry_malloc ( sizeof *newlist + n );
+ if (!newlist)
+ return NULL;
+ d = newlist->d;
+ memcpy ( d, head, n ); d += n;
+ *d++ = ST_STOP;
+ }
+ else
+ newlist = NULL;
+
+ return normalize (newlist);
+}
+
+gcry_sexp_t
+gcry_sexp_car( const gcry_sexp_t list )
+{
+ 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)
+{
+ const byte *p;
+ DATALEN n;
+ int level = 0;
+
+ *datalen = 0;
+ if ( !list )
+ return NULL;
+
+ p = list->d;
+ if ( *p == ST_OPEN )
+ p++; /* Yep, a list. */
+ else if (number)
+ return NULL; /* Not a list but N > 0 requested. */
+
+ /* Skip over N elements. */
+ while ( number > 0 )
+ {
+ 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_CLOSE )
+ {
+ level--;
+ if ( !level )
+ number--;
+ }
+ else if ( *p == ST_STOP )
+ {
+ return NULL;
+ }
+ p++;
+ }
+
+ /* If this is data, return it. */
+ if ( *p == ST_DATA )
+ {
+ memcpy ( &n, ++p, sizeof n );
+ *datalen = n;
+ return (const char*)p + sizeof n;
+ }
+
+ return NULL;
+}
+
+
+/* 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 )
+{
+ return sexp_nth_data (list, number, datalen);
+}
+
+
+/* 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)
+{
+ const char *s;
+ size_t n;
+ char *buf;
+
+ s = sexp_nth_data (list, number, &n);
+ if (!s || n < 1 || (n+1) < 1)
+ return NULL;
+ buf = gcry_malloc (n+1);
+ if (!buf)
+ return NULL;
+ memcpy (buf, s, n);
+ buf[n] = 0;
+ return buf;
+}
+
+/*
+ * Get a MPI from the car
+ */
+gcry_mpi_t
+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 ( 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 )
+{
+ 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_CLOSE ) {
+ level--;
+ if ( !level )
+ skip--;
+ }
+ 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;
+
+ 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);
+}
+
+gcry_sexp_t
+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 );
+ return b;
+}
+
+
+
+static int
+hextobyte( const byte *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;
+}
+
+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 )
+{
+ size_t used = c->pos - c->sexp->d;
+
+ if ( used + n + sizeof(DATALEN) + 1 >= c->allocated )
+ {
+ gcry_sexp_t newsexp;
+ byte *newhead;
+ size_t newsize;
+
+ 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);
+ if (!newsexp)
+ return gpg_err_code_from_errno (errno);
+ c->allocated = newsize;
+ newhead = newsexp->d;
+ c->pos = newhead + used;
+ c->sexp = newsexp;
+ }
+ return 0;
+}
+
+
+/* Unquote STRING of LENGTH and store it into BUF. The surrounding
+ quotes are must already be removed from STRING. We assume that the
+ quoted string is syntacillay correct. */
+static size_t
+unquote_string (const char *string, size_t length, unsigned char *buf)
+{
+ int esc = 0;
+ const unsigned char *s = (const unsigned char*)string;
+ unsigned char *d = buf;
+ size_t n = length;
+
+ for (; n; n--, s++)
+ {
+ if (esc)
+ {
+ switch (*s)
+ {
+ case 'b': *d++ = '\b'; break;
+ case 't': *d++ = '\t'; break;
+ case 'v': *d++ = '\v'; break;
+ case 'n': *d++ = '\n'; break;
+ case 'f': *d++ = '\f'; break;
+ case 'r': *d++ = '\r'; break;
+ case '"': *d++ = '\"'; break;
+ case '\'': *d++ = '\''; break;
+ case '\\': *d++ = '\\'; break;
+
+ case '\r': /* ignore CR[,LF] */
+ if (n>1 && s[1] == '\n')
+ {
+ s++; n--;
+ }
+ break;
+
+ case '\n': /* ignore LF[,CR] */
+ if (n>1 && s[1] == '\r')
+ {
+ s++; n--;
+ }
+ break;
+
+ case 'x': /* hex value */
+ if (n>2 && hexdigitp (s+1) && hexdigitp (s+2))
+ {
+ s++; n--;
+ *d++ = xtoi_2 (s);
+ s++; n--;
+ }
+ break;
+
+ default:
+ if (n>2 && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2))
+ {
+ *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2);
+ s += 2;
+ n -= 2;
+ }
+ break;
+ }
+ esc = 0;
+ }
+ else if( *s == '\\' )
+ esc = 1;
+ else
+ *d++ = *s;
+ }
+
+ return d - buf;
+}
+
+/****************
+ * Scan the provided buffer and return the S expression in our internal
+ * format. Returns a newly allocated expression. If erroff is not NULL and
+ * a parsing error has occurred, the offset into buffer will be returned.
+ * If ARGFLAG is true, the function supports some printf like
+ * expressions.
+ * These are:
+ * %m - MPI
+ * %s - string (no autoswitch to secure allocation)
+ * %d - integer stored as string (no autoswitch to secure allocation)
+ * %b - memory buffer; this takes _two_ arguments: an integer with the
+ * length of the buffer and a pointer to the buffer.
+ * %S - Copy an gcry_sexp_t here. The S-expression needs to be a
+ * regular one, starting with a parenthesis.
+ * (no autoswitch to secure allocation)
+ * all other format elements are currently not defined and return an error.
+ * this includes the "%%" sequence becauce the percent sign is not an
+ * allowed character.
+ * FIXME: We should find a way to store the secure-MPIs not in the string
+ * but as reference to somewhere - this can help us to save huge amounts
+ * of secure memory. The problem is, that if only one element is secure, all
+ * other elements are automagicaly copied to secure memory too, so the most
+ * 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)
+{
+ gcry_err_code_t err = 0;
+ static const char tokenchars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789-./_:*+=";
+ const char *p;
+ size_t n;
+ const char *digptr = NULL;
+ const char *quoted = NULL;
+ const char *tokenp = NULL;
+ const char *hexfmt = NULL;
+ const char *base64 = NULL;
+ const char *disphint = NULL;
+ const char *percent = NULL;
+ int hexcount = 0;
+ int quoted_esc = 0;
+ int datalen = 0;
+ size_t dummy_erroff;
+ struct make_space_ctx c;
+ int arg_counter = 0;
+ int level = 0;
+
+ if (!erroff)
+ erroff = &dummy_erroff;
+
+ /* Depending on whether ARG_LIST is non-zero or not, this macro gives
+ us the next argument, either from the variable argument list as
+ specified by ARG_PTR or from the argument array ARG_LIST. */
+#define ARG_NEXT(storage, type) \
+ do \
+ { \
+ if (!arg_list) \
+ storage = va_arg (arg_ptr, type); \
+ else \
+ storage = *((type *) (arg_list[arg_counter++])); \
+ } \
+ while (0)
+
+ /* The MAKE_SPACE macro is used before each store operation to
+ ensure that the buffer is large enough. It requires a global
+ context named C and jumps out to the label LEAVE on error! It
+ also sets ERROFF using the variables BUFFER and P. */
+#define MAKE_SPACE(n) do { \
+ gpg_err_code_t _ms_err = make_space (&c, (n)); \
+ if (_ms_err) \
+ { \
+ err = _ms_err; \
+ *erroff = p - buffer; \
+ goto leave; \
+ } \
+ } while (0)
+
+ /* The STORE_LEN macro is used to store the length N at buffer P. */
+#define STORE_LEN(p,n) do { \
+ DATALEN ashort = (n); \
+ memcpy ( (p), &ashort, sizeof(ashort) ); \
+ (p) += sizeof (ashort); \
+ } while (0)
+
+ /* We assume that the internal representation takes less memory than
+ 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);
+ else
+ c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1);
+ if (!c.sexp)
+ {
+ err = gpg_err_code_from_errno (errno);
+ *erroff = 0;
+ goto leave;
+ }
+ c.pos = c.sexp->d;
+
+ for (p = buffer, n = length; n; p++, n--)
+ {
+ if (tokenp && !hexfmt)
+ {
+ if (strchr (tokenchars, *p))
+ continue;
+ else
+ {
+ datalen = p - tokenp;
+ MAKE_SPACE (datalen);
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, datalen);
+ memcpy (c.pos, tokenp, datalen);
+ c.pos += datalen;
+ tokenp = NULL;
+ }
+ }
+
+ if (quoted)
+ {
+ if (quoted_esc)
+ {
+ switch (*p)
+ {
+ case 'b': case 't': case 'v': case 'n': case 'f':
+ case 'r': case '"': case '\'': case '\\':
+ quoted_esc = 0;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ if (!((n > 2)
+ && (p[1] >= '0') && (p[1] <= '7')
+ && (p[2] >= '0') && (p[2] <= '7')))
+ {
+ *erroff = p - buffer;
+ /* Invalid octal value. */
+ err = GPG_ERR_SEXP_BAD_QUOTATION;
+ goto leave;
+ }
+ p += 2;
+ n -= 2;
+ quoted_esc = 0;
+ break;
+
+ case 'x':
+ if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2)))
+ {
+ *erroff = p - buffer;
+ /* Invalid hex value. */
+ err = GPG_ERR_SEXP_BAD_QUOTATION;
+ goto leave;
+ }
+ p += 2;
+ n -= 2;
+ quoted_esc = 0;
+ break;
+
+ case '\r':
+ /* ignore CR[,LF] */
+ if (n && (p[1] == '\n'))
+ {
+ p++;
+ n--;
+ }
+ quoted_esc = 0;
+ break;
+
+ case '\n':
+ /* ignore LF[,CR] */
+ if (n && (p[1] == '\r'))
+ {
+ p++;
+ n--;
+ }
+ quoted_esc = 0;
+ break;
+
+ default:
+ *erroff = p - buffer;
+ /* Invalid quoted string escape. */
+ err = GPG_ERR_SEXP_BAD_QUOTATION;
+ goto leave;
+ }
+ }
+ else if (*p == '\\')
+ quoted_esc = 1;
+ else if (*p == '\"')
+ {
+ /* Keep it easy - we know that the unquoted string will
+ never be larger. */
+ unsigned char *save;
+ size_t len;
+
+ quoted++; /* Skip leading quote. */
+ MAKE_SPACE (p - quoted);
+ *c.pos++ = ST_DATA;
+ save = c.pos;
+ STORE_LEN (c.pos, 0); /* Will be fixed up later. */
+ len = unquote_string (quoted, p - quoted, c.pos);
+ c.pos += len;
+ STORE_LEN (save, len);
+ quoted = NULL;
+ }
+ }
+ else if (hexfmt)
+ {
+ if (isxdigit (*p))
+ hexcount++;
+ else if (*p == '#')
+ {
+ if ((hexcount & 1))
+ {
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_ODD_HEX_NUMBERS;
+ goto leave;
+ }
+
+ datalen = hexcount / 2;
+ MAKE_SPACE (datalen);
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, datalen);
+ for (hexfmt++; hexfmt < p; hexfmt++)
+ {
+ if (whitespacep (hexfmt))
+ continue;
+ *c.pos++ = hextobyte ((const unsigned char*)hexfmt);
+ hexfmt++;
+ }
+ hexfmt = NULL;
+ }
+ else if (!whitespacep (p))
+ {
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_BAD_HEX_CHAR;
+ goto leave;
+ }
+ }
+ else if (base64)
+ {
+ if (*p == '|')
+ base64 = NULL;
+ }
+ else if (digptr)
+ {
+ if (digitp (p))
+ ;
+ else if (*p == ':')
+ {
+ datalen = atoi (digptr); /* FIXME: check for overflow. */
+ digptr = NULL;
+ if (datalen > n - 1)
+ {
+ *erroff = p - buffer;
+ /* Buffer too short. */
+ err = GPG_ERR_SEXP_STRING_TOO_LONG;
+ goto leave;
+ }
+ /* Make a new list entry. */
+ MAKE_SPACE (datalen);
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, datalen);
+ memcpy (c.pos, p + 1, datalen);
+ c.pos += datalen;
+ n -= datalen;
+ p += datalen;
+ }
+ else if (*p == '\"')
+ {
+ digptr = NULL; /* We ignore the optional length. */
+ quoted = p;
+ quoted_esc = 0;
+ }
+ else if (*p == '#')
+ {
+ digptr = NULL; /* We ignore the optional length. */
+ hexfmt = p;
+ hexcount = 0;
+ }
+ else if (*p == '|')
+ {
+ digptr = NULL; /* We ignore the optional length. */
+ base64 = p;
+ }
+ else
+ {
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_INV_LEN_SPEC;
+ goto leave;
+ }
+ }
+ else if (percent)
+ {
+ if (*p == 'm')
+ {
+ /* Insert an MPI. */
+ gcry_mpi_t m;
+ size_t nm = 0;
+
+ ARG_NEXT (m, gcry_mpi_t);
+
+ if (gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &nm, m))
+ BUG ();
+
+ MAKE_SPACE (nm);
+ if (!gcry_is_secure (c.sexp->d)
+ && gcry_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);
+ if (!newsexp)
+ {
+ err = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ 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);
+ c.sexp = newsexp;
+ }
+
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, nm);
+ if (gcry_mpi_print (GCRYMPI_FMT_STD, c.pos, nm, &nm, m))
+ BUG ();
+ c.pos += nm;
+ }
+ else if (*p == 's')
+ {
+ /* Insert an string. */
+ const char *astr;
+ size_t alen;
+
+ ARG_NEXT (astr, const char *);
+ alen = strlen (astr);
+
+ MAKE_SPACE (alen);
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, alen);
+ memcpy (c.pos, astr, alen);
+ c.pos += alen;
+ }
+ else if (*p == 'b')
+ {
+ /* Insert a memory buffer. */
+ const char *astr;
+ int alen;
+
+ ARG_NEXT (alen, int);
+ ARG_NEXT (astr, const char *);
+
+ MAKE_SPACE (alen);
+ if (alen
+ && !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);
+ if (!newsexp)
+ {
+ err = gpg_err_code_from_errno (errno);
+ goto leave;
+ }
+ 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);
+ c.sexp = newsexp;
+ }
+
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, alen);
+ memcpy (c.pos, astr, alen);
+ c.pos += alen;
+ }
+ else if (*p == 'd')
+ {
+ /* Insert an integer as string. */
+ int aint;
+ size_t alen;
+ char buf[20];
+
+ ARG_NEXT (aint, int);
+ sprintf (buf, "%d", 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. */
+ gcry_sexp_t asexp;
+ size_t alen, aoff;
+
+ ARG_NEXT (asexp, gcry_sexp_t);
+ alen = get_internal_buffer (asexp, &aoff);
+ if (alen)
+ {
+ MAKE_SPACE (alen);
+ memcpy (c.pos, asexp->d + aoff, alen);
+ c.pos += alen;
+ }
+ }
+ else
+ {
+ *erroff = p - buffer;
+ /* Invalid format specifier. */
+ err = GPG_ERR_SEXP_INV_LEN_SPEC;
+ goto leave;
+ }
+ percent = NULL;
+ }
+ else if (*p == '(')
+ {
+ if (disphint)
+ {
+ *erroff = p - buffer;
+ /* Open display hint. */
+ err = GPG_ERR_SEXP_UNMATCHED_DH;
+ goto leave;
+ }
+ MAKE_SPACE (0);
+ *c.pos++ = ST_OPEN;
+ level++;
+ }
+ else if (*p == ')')
+ {
+ /* Walk up. */
+ if (disphint)
+ {
+ *erroff = p - buffer;
+ /* Open display hint. */
+ err = GPG_ERR_SEXP_UNMATCHED_DH;
+ goto leave;
+ }
+ MAKE_SPACE (0);
+ *c.pos++ = ST_CLOSE;
+ level--;
+ }
+ else if (*p == '\"')
+ {
+ quoted = p;
+ quoted_esc = 0;
+ }
+ else if (*p == '#')
+ {
+ hexfmt = p;
+ hexcount = 0;
+ }
+ else if (*p == '|')
+ base64 = p;
+ else if (*p == '[')
+ {
+ if (disphint)
+ {
+ *erroff = p - buffer;
+ /* Open display hint. */
+ err = GPG_ERR_SEXP_NESTED_DH;
+ goto leave;
+ }
+ disphint = p;
+ }
+ else if (*p == ']')
+ {
+ if (!disphint)
+ {
+ *erroff = p - buffer;
+ /* Open display hint. */
+ err = GPG_ERR_SEXP_UNMATCHED_DH;
+ goto leave;
+ }
+ disphint = NULL;
+ }
+ else if (digitp (p))
+ {
+ if (*p == '0')
+ {
+ /* A length may not begin with zero. */
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_ZERO_PREFIX;
+ goto leave;
+ }
+ digptr = p;
+ }
+ else if (strchr (tokenchars, *p))
+ tokenp = p;
+ else if (whitespacep (p))
+ ;
+ else if (*p == '{')
+ {
+ /* fixme: handle rescanning: we can do this by saving our
+ current state and start over at p+1 -- Hmmm. At this
+ point here we are in a well defined state, so we don't
+ need to save it. Great. */
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+ goto leave;
+ }
+ else if (strchr ("&\\", *p))
+ {
+ /* Reserved punctuation. */
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+ goto leave;
+ }
+ else if (argflag && (*p == '%'))
+ percent = p;
+ else
+ {
+ /* Bad or unavailable. */
+ *erroff = p - buffer;
+ err = GPG_ERR_SEXP_BAD_CHARACTER;
+ goto leave;
+ }
+ }
+ MAKE_SPACE (0);
+ *c.pos++ = ST_STOP;
+
+ if (level && !err)
+ err = GPG_ERR_SEXP_UNMATCHED_PAREN;
+
+ leave:
+ if (err)
+ {
+ /* Error -> deallocate. */
+ if (c.sexp)
+ {
+ /* Extra paranoid wipe on error. */
+ if (gcry_is_secure (c.sexp))
+ wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1);
+ gcry_free (c.sexp);
+ }
+ /* This might be expected by existing code... */
+ *retsexp = NULL;
+ }
+ else
+ *retsexp = normalize (c.sexp);
+
+ return gcry_error (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, ...)
+{
+ gcry_error_t rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, arg_list);
+ rc = 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, ...)
+{
+ gcry_error_t rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ rc = vsexp_sscan (retsexp, erroff, format, strlen(format), 1,
+ NULL, arg_ptr);
+ va_end (arg_ptr);
+
+ return rc;
+}
+
+
+gcry_error_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);
+}
+
+
+/* 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)
+{
+ return 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)
+{
+ return sexp_sscan (retsexp, erroff, buffer, length, 0, NULL);
+}
+
+
+/* Figure out a suitable encoding for BUFFER of LENGTH.
+ Returns: 0 = Binary
+ 1 = String possible
+ 2 = Token possible
+*/
+static int
+suitable_encoding (const unsigned char *buffer, size_t length)
+{
+ const unsigned char *s;
+ int maybe_token = 1;
+
+ if (!length)
+ return 1;
+
+ for (s=buffer; length; s++, length--)
+ {
+ if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0))
+ && !strchr ("\b\t\v\n\f\r\"\'\\", *s))
+ return 0; /*binary*/
+ if ( maybe_token
+ && !alphap (s) && !digitp (s) && !strchr (TOKEN_SPECIALS, *s))
+ maybe_token = 0;
+ }
+ s = buffer;
+ if ( maybe_token && !digitp (s) )
+ return 2;
+ return 1;
+}
+
+
+static int
+convert_to_hex (const unsigned char *src, size_t len, char *dest)
+{
+ int i;
+
+ if (dest)
+ {
+ *dest++ = '#';
+ for (i=0; i < len; i++, dest += 2 )
+ sprintf (dest, "%02X", src[i]);
+ *dest++ = '#';
+ }
+ return len*2+2;
+}
+
+static int
+convert_to_string (const unsigned char *s, size_t len, char *dest)
+{
+ if (dest)
+ {
+ char *p = dest;
+ *p++ = '\"';
+ for (; len; len--, s++ )
+ {
+ switch (*s)
+ {
+ case '\b': *p++ = '\\'; *p++ = 'b'; break;
+ case '\t': *p++ = '\\'; *p++ = 't'; break;
+ case '\v': *p++ = '\\'; *p++ = 'v'; break;
+ case '\n': *p++ = '\\'; *p++ = 'n'; break;
+ case '\f': *p++ = '\\'; *p++ = 'f'; break;
+ case '\r': *p++ = '\\'; *p++ = 'r'; break;
+ case '\"': *p++ = '\\'; *p++ = '\"'; break;
+ case '\'': *p++ = '\\'; *p++ = '\''; break;
+ case '\\': *p++ = '\\'; *p++ = '\\'; break;
+ default:
+ if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0)))
+ {
+ sprintf (p, "\\x%02x", *s);
+ p += 4;
+ }
+ else
+ *p++ = *s;
+ }
+ }
+ *p++ = '\"';
+ return p - dest;
+ }
+ else
+ {
+ int count = 2;
+ for (; len; len--, s++ )
+ {
+ switch (*s)
+ {
+ case '\b':
+ case '\t':
+ case '\v':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\"':
+ case '\'':
+ case '\\': count += 2; break;
+ default:
+ if ( (*s < 0x20 || (*s >= 0x7f && *s <= 0xa0)))
+ count += 4;
+ else
+ count++;
+ }
+ }
+ return count;
+ }
+}
+
+
+
+static int
+convert_to_token (const unsigned char *src, size_t len, char *dest)
+{
+ if (dest)
+ memcpy (dest, src, len);
+ return len;
+}
+
+
+/****************
+ * Print SEXP to buffer using the MODE. Returns the length of the
+ * SEXP in buffer or 0 if the buffer is too short (We have at least an
+ * empty list consisting of 2 bytes). If a buffer of NULL is provided,
+ * the required length is returned.
+ */
+size_t
+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;
+ char *d;
+ DATALEN n;
+ char numbuf[20];
+ size_t len = 0;
+ int i, indent = 0;
+
+ s = list? list->d : empty;
+ d = buffer;
+ while ( *s != ST_STOP )
+ {
+ switch ( *s )
+ {
+ case ST_OPEN:
+ s++;
+ if ( mode != GCRYSEXP_FMT_CANON )
+ {
+ if (indent)
+ len++;
+ len += indent;
+ }
+ len++;
+ if ( buffer )
+ {
+ if ( len >= maxlength )
+ return 0;
+ if ( mode != GCRYSEXP_FMT_CANON )
+ {
+ if (indent)
+ *d++ = '\n';
+ for (i=0; i < indent; i++)
+ *d++ = ' ';
+ }
+ *d++ = '(';
+ }
+ indent++;
+ break;
+ case ST_CLOSE:
+ s++;
+ len++;
+ if ( buffer )
+ {
+ if ( len >= maxlength )
+ return 0;
+ *d++ = ')';
+ }
+ indent--;
+ if (*s != ST_OPEN && *s != ST_STOP && mode != GCRYSEXP_FMT_CANON)
+ {
+ len++;
+ len += indent;
+ if (buffer)
+ {
+ if (len >= maxlength)
+ return 0;
+ *d++ = '\n';
+ for (i=0; i < indent; i++)
+ *d++ = ' ';
+ }
+ }
+ break;
+ case ST_DATA:
+ s++;
+ memcpy ( &n, s, sizeof n ); s += sizeof n;
+ if (mode == GCRYSEXP_FMT_ADVANCED)
+ {
+ int type;
+ size_t nn;
+
+ switch ( (type=suitable_encoding (s, n)))
+ {
+ case 1: nn = convert_to_string (s, n, NULL); break;
+ case 2: nn = convert_to_token (s, n, NULL); break;
+ default: nn = convert_to_hex (s, n, NULL); break;
+ }
+ len += nn;
+ if (buffer)
+ {
+ if (len >= maxlength)
+ return 0;
+ switch (type)
+ {
+ case 1: convert_to_string (s, n, d); break;
+ case 2: convert_to_token (s, n, d); break;
+ default: convert_to_hex (s, n, d); break;
+ }
+ d += nn;
+ }
+ if (s[n] != ST_CLOSE)
+ {
+ len++;
+ if (buffer)
+ {
+ if (len >= maxlength)
+ return 0;
+ *d++ = ' ';
+ }
+ }
+ }
+ else
+ {
+ sprintf (numbuf, "%u:", (unsigned int)n );
+ len += strlen (numbuf) + n;
+ if ( buffer )
+ {
+ if ( len >= maxlength )
+ return 0;
+ d = stpcpy ( d, numbuf );
+ memcpy ( d, s, n ); d += n;
+ }
+ }
+ s += n;
+ break;
+ default:
+ BUG ();
+ }
+ }
+ if ( mode != GCRYSEXP_FMT_CANON )
+ {
+ len++;
+ if (buffer)
+ {
+ if ( len >= maxlength )
+ return 0;
+ *d++ = '\n';
+ }
+ }
+ if (buffer)
+ {
+ if ( len >= maxlength )
+ return 0;
+ *d++ = 0; /* for convenience we make a C string */
+ }
+ else
+ len++; /* we need one byte more for this */
+
+ return len;
+}
+
+
+/* Scan a canonical encoded buffer with implicit length values and
+ return the actual length this S-expression uses. For a valid S-Exp
+ it should never return 0. If LENGTH is not zero, the maximum
+ length to scan is given - this can be used for syntax checks of
+ 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)
+{
+ const unsigned char *p;
+ const unsigned char *disphint = NULL;
+ unsigned int datalen = 0;
+ size_t dummy_erroff;
+ gcry_error_t dummy_errcode;
+ size_t count = 0;
+ int level = 0;
+
+ if (!erroff)
+ erroff = &dummy_erroff;
+ if (!errcode)
+ errcode = &dummy_errcode;
+
+ *errcode = gcry_error (GPG_ERR_NO_ERROR);
+ *erroff = 0;
+ if (!buffer)
+ return 0;
+ if (*buffer != '(')
+ {
+ *errcode = gcry_error (GPG_ERR_SEXP_NOT_CANONICAL);
+ return 0;
+ }
+
+ for (p=buffer; ; p++, count++ )
+ {
+ if (length && count >= length)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+ return 0;
+ }
+
+ if (datalen)
+ {
+ if (*p == ':')
+ {
+ if (length && (count+datalen) >= length)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+ return 0;
+ }
+ count += datalen;
+ p += datalen;
+ datalen = 0;
+ }
+ else if (digitp(p))
+ datalen = datalen*10 + atoi_1(p);
+ else
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_INV_LEN_SPEC);
+ return 0;
+ }
+ }
+ else if (*p == '(')
+ {
+ if (disphint)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ return 0;
+ }
+ level++;
+ }
+ else if (*p == ')')
+ { /* walk up */
+ if (!level)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_PAREN);
+ return 0;
+ }
+ if (disphint)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ return 0;
+ }
+ if (!--level)
+ return ++count; /* ready */
+ }
+ else if (*p == '[')
+ {
+ if (disphint)
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_NESTED_DH);
+ return 0;
+ }
+ disphint = p;
+ }
+ else if (*p == ']')
+ {
+ if ( !disphint )
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
+ return 0;
+ }
+ disphint = NULL;
+ }
+ else if (digitp (p) )
+ {
+ if (*p == '0')
+ {
+ *erroff = count;
+ *errcode = gcry_error (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);
+ return 0;
+ }
+ else
+ {
+ *erroff = count;
+ *errcode = gcry_error (GPG_ERR_SEXP_BAD_CHARACTER);
+ return 0;
+ }
+ }
+}
|