Imported Upstream version 2.0.19
[platform/upstream/gpg2.git] / common / tlv.c
1 /* tlv.c - Tag-Length-Value Utilities
2  *      Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #if GNUPG_MAJOR_VERSION == 1
28 #define GPG_ERR_EOF               (-1)
29 #define GPG_ERR_BAD_BER           (1)  /*G10ERR_GENERAL*/
30 #define GPG_ERR_INV_SEXP          (45) /*G10ERR_INV_ARG*/
31 typedef int gpg_error_t;
32 #define gpg_make_err(x,n) (n)
33 #else
34 #include <gpg-error.h>
35 #endif
36
37 #include "tlv.h"
38
39 static const unsigned char *
40 do_find_tlv (const unsigned char *buffer, size_t length,
41              int tag, size_t *nbytes, int nestlevel)
42 {
43   const unsigned char *s = buffer;
44   size_t n = length;
45   size_t len;
46   int this_tag;
47   int composite;
48     
49   for (;;)
50     {
51       buffer = s;
52       if (n < 2)
53         return NULL; /* Buffer definitely too short for tag and length. */
54       if (!*s || *s == 0xff)
55         { /* Skip optional filler between TLV objects. */
56           s++;
57           n--;
58           continue;
59         }
60       composite = !!(*s & 0x20);
61       if ((*s & 0x1f) == 0x1f)
62         { /* more tag bytes to follow */
63           s++;
64           n--;
65           if (n < 2)
66             return NULL; /* buffer definitely too short for tag and length. */
67           if ((*s & 0x1f) == 0x1f)
68             return NULL; /* We support only up to 2 bytes. */
69           this_tag = (s[-1] << 8) | (s[0] & 0x7f);
70         }
71       else
72         this_tag = s[0];
73       len = s[1];
74       s += 2; n -= 2;
75       if (len < 0x80)
76         ;
77       else if (len == 0x81)
78         { /* One byte length follows. */
79           if (!n)
80             return NULL; /* we expected 1 more bytes with the length. */
81           len = s[0];
82           s++; n--;
83         }
84       else if (len == 0x82)
85         { /* Two byte length follows. */
86           if (n < 2)
87             return NULL; /* We expected 2 more bytes with the length. */
88           len = (s[0] << 8) | s[1];
89           s += 2; n -= 2;
90         }
91       else
92         return NULL; /* APDU limit is 65535, thus it does not make
93                         sense to assume longer length fields. */
94
95       if (composite && nestlevel < 100)
96         { /* Dive into this composite DO after checking for a too deep
97              nesting. */
98           const unsigned char *tmp_s;
99           size_t tmp_len;
100           
101           tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
102           if (tmp_s)
103             {
104               *nbytes = tmp_len;
105               return tmp_s;
106             }
107         }
108
109       if (this_tag == tag)
110         {
111           *nbytes = len;
112           return s;
113         }
114       if (len > n)
115         return NULL; /* Buffer too short to skip to the next tag. */
116       s += len; n -= len;
117     }
118 }
119
120
121 /* Locate a TLV encoded data object in BUFFER of LENGTH and
122    return a pointer to value as well as its length in NBYTES.  Return
123    NULL if it was not found or if the object does not fit into the buffer. */
124 const unsigned char *
125 find_tlv (const unsigned char *buffer, size_t length,
126           int tag, size_t *nbytes)
127 {
128   const unsigned char *p;
129
130   p = do_find_tlv (buffer, length, tag, nbytes, 0);
131   if (p && *nbytes > (length - (p-buffer)))
132     p = NULL; /* Object longer than buffer. */
133   return p;
134 }
135
136
137
138 /* Locate a TLV encoded data object in BUFFER of LENGTH and
139    return a pointer to value as well as its length in NBYTES.  Return
140    NULL if it was not found.  Note, that the function does not check
141    whether the value fits into the provided buffer. */
142 const unsigned char *
143 find_tlv_unchecked (const unsigned char *buffer, size_t length,
144                     int tag, size_t *nbytes)
145 {
146   return do_find_tlv (buffer, length, tag, nbytes, 0);
147 }
148
149
150 /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
151    and the length part from the TLV triplet.  Update BUFFER and SIZE
152    on success. */
153 gpg_error_t
154 _parse_ber_header (unsigned char const **buffer, size_t *size,
155                    int *r_class, int *r_tag, 
156                    int *r_constructed, int *r_ndef,
157                    size_t *r_length, size_t *r_nhdr,
158                    gpg_err_source_t errsource)
159 {
160   int c;
161   unsigned long tag;
162   const unsigned char *buf = *buffer;
163   size_t length = *size;
164
165   *r_ndef = 0;
166   *r_length = 0;
167   *r_nhdr = 0;
168
169   /* Get the tag. */
170   if (!length)
171     return gpg_err_make (errsource, GPG_ERR_EOF);
172   c = *buf++; length--; ++*r_nhdr;
173
174   *r_class = (c & 0xc0) >> 6;
175   *r_constructed = !!(c & 0x20);
176   tag = c & 0x1f;
177
178   if (tag == 0x1f)
179     {
180       tag = 0;
181       do
182         {
183           tag <<= 7;
184           if (!length)
185             return gpg_err_make (errsource, GPG_ERR_EOF);
186           c = *buf++; length--; ++*r_nhdr;
187           tag |= c & 0x7f;
188
189         }
190       while (c & 0x80);
191     }
192   *r_tag = tag;
193
194   /* Get the length. */
195   if (!length)
196     return gpg_err_make (errsource, GPG_ERR_EOF);
197   c = *buf++; length--; ++*r_nhdr;
198
199   if ( !(c & 0x80) )
200     *r_length = c;
201   else if (c == 0x80)
202     *r_ndef = 1;
203   else if (c == 0xff)
204     return gpg_err_make (errsource, GPG_ERR_BAD_BER);
205   else
206     {
207       unsigned long len = 0;
208       int count = c & 0x7f;
209
210       if (count > sizeof (len) || count > sizeof (size_t))
211         return gpg_err_make (errsource, GPG_ERR_BAD_BER);
212
213       for (; count; count--)
214         {
215           len <<= 8;
216           if (!length)
217             return gpg_err_make (errsource, GPG_ERR_EOF);
218           c = *buf++; length--; ++*r_nhdr;
219           len |= c & 0xff;
220         }
221       *r_length = len;
222     }
223   
224   /* Without this kludge some example certs can't be parsed. */
225   if (*r_class == CLASS_UNIVERSAL && !*r_tag)
226     *r_length = 0;
227   
228   *buffer = buf;
229   *size = length;
230   return 0;
231 }
232
233
234 /* FIXME: The following function should not go into this file but for
235    now it is easier to keep it here. */
236
237 /* Return the next token of an canconical encoded S-expression.  BUF
238    is the pointer to the S-expression and BUFLEN is a pointer to the
239    length of this S-expression (used to validate the syntax).  Both
240    are updated to reflect the new position.  The token itself is
241    returned as a pointer into the orginal buffer at TOK and TOKLEN.
242    If a parentheses is the next token, TOK will be set to NULL.
243    TOKLEN is checked to be within the bounds.  On error a error code
244    is returned and all pointers should are not guaranteed to point to
245    a meanigful value. DEPTH should be initialized to 0 and will
246    reflect on return the actual depth of the tree. To detect the end
247    of the S-expression it is advisable to check DEPTH after a
248    successful return:
249
250    depth = 0;
251    while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
252           && depth)
253      process_token (tok, toklen);
254    if (err)  
255      handle_error ();
256  */
257 gpg_error_t
258 _parse_sexp (unsigned char const **buf, size_t *buflen,
259              int *depth, unsigned char const **tok, size_t *toklen,
260              gpg_err_source_t errsource)
261 {
262   const unsigned char *s;
263   size_t n, vlen;
264
265   s = *buf;
266   n = *buflen;
267   *tok = NULL;
268   *toklen = 0;
269   if (!n)
270     return *depth ? gpg_err_make (errsource, GPG_ERR_INV_SEXP) : 0;
271   if (*s == '(')
272     {
273       s++; n--;
274       (*depth)++;
275       *buf = s;
276       *buflen = n;
277       return 0;
278     }
279   if (*s == ')')
280     {
281       if (!*depth)
282         return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
283       *toklen = 1;
284       s++; n--;
285       (*depth)--;
286       *buf = s;
287       *buflen = n;
288       return 0;
289     }
290   for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
291     vlen = vlen*10 + (*s - '0');
292   if (!n || *s != ':')
293     return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
294   s++; n--;
295   if (vlen > n)
296     return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
297   *tok = s;
298   *toklen = vlen;
299   s += vlen;
300   n -= vlen;
301   *buf = s;
302   *buflen = n;
303   return 0;
304 }
305