Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18826323
sexp.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
46 KB
Subscribers
None
sexp.c
View Options
/* 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 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
;
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Dec 23, 5:05 PM (4 h, 58 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
30/c3/04a24ef42ef3cec51bb3775f2a45
Attached To
rC libgcrypt
Event Timeline
Log In to Comment