Switched to GPLv3.
[gnupg.git] / g10 / 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_error(n) (n)
33 #else
34 #include <gpg-error.h>
35 #endif
36
37
38 #include "tlv.h"
39
40 static const unsigned char *
41 do_find_tlv (const unsigned char *buffer, size_t length,
42              int tag, size_t *nbytes, int nestlevel)
43 {
44   const unsigned char *s = buffer;
45   size_t n = length;
46   size_t len;
47   int this_tag;
48   int composite;
49     
50   for (;;)
51     {
52       buffer = s;
53       if (n < 2)
54         return NULL; /* Buffer definitely too short for tag and length. */
55       if (!*s || *s == 0xff)
56         { /* Skip optional filler between TLV objects. */
57           s++;
58           n--;
59           continue;
60         }
61       composite = !!(*s & 0x20);
62       if ((*s & 0x1f) == 0x1f)
63         { /* more tag bytes to follow */
64           s++;
65           n--;
66           if (n < 2)
67             return NULL; /* buffer definitely too short for tag and length. */
68           if ((*s & 0x1f) == 0x1f)
69             return NULL; /* We support only up to 2 bytes. */
70           this_tag = (s[-1] << 8) | (s[0] & 0x7f);
71         }
72       else
73         this_tag = s[0];
74       len = s[1];
75       s += 2; n -= 2;
76       if (len < 0x80)
77         ;
78       else if (len == 0x81)
79         { /* One byte length follows. */
80           if (!n)
81             return NULL; /* we expected 1 more bytes with the length. */
82           len = s[0];
83           s++; n--;
84         }
85       else if (len == 0x82)
86         { /* Two byte length follows. */
87           if (n < 2)
88             return NULL; /* We expected 2 more bytes with the length. */
89           len = (s[0] << 8) | s[1];
90           s += 2; n -= 2;
91         }
92       else
93         return NULL; /* APDU limit is 65535, thus it does not make
94                         sense to assume longer length fields. */
95
96       if (composite && nestlevel < 100)
97         { /* Dive into this composite DO after checking for a too deep
98              nesting. */
99           const unsigned char *tmp_s;
100           size_t tmp_len;
101           
102           tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
103           if (tmp_s)
104             {
105               *nbytes = tmp_len;
106               return tmp_s;
107             }
108         }
109
110       if (this_tag == tag)
111         {
112           *nbytes = len;
113           return s;
114         }
115       if (len > n)
116         return NULL; /* Buffer too short to skip to the next tag. */
117       s += len; n -= len;
118     }
119 }
120
121
122 /* Locate a TLV encoded data object in BUFFER of LENGTH and
123    return a pointer to value as well as its length in NBYTES.  Return
124    NULL if it was not found or if the object does not fit into the buffer. */
125 const unsigned char *
126 find_tlv (const unsigned char *buffer, size_t length,
127           int tag, size_t *nbytes)
128 {
129   const unsigned char *p;
130
131   p = do_find_tlv (buffer, length, tag, nbytes, 0);
132   if (p && *nbytes > (length - (p-buffer)))
133     p = NULL; /* Object longer than buffer. */
134   return p;
135 }
136
137
138
139 /* Locate a TLV encoded data object in BUFFER of LENGTH and
140    return a pointer to value as well as its length in NBYTES.  Return
141    NULL if it was not found.  Note, that the function does not check
142    whether the value fits into the provided buffer. */
143 const unsigned char *
144 find_tlv_unchecked (const unsigned char *buffer, size_t length,
145                     int tag, size_t *nbytes)
146 {
147   return do_find_tlv (buffer, length, tag, nbytes, 0);
148 }
149
150
151 /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
152    and the length part from the TLV triplet.  Update BUFFER and SIZE
153    on success. */
154 gpg_error_t
155 parse_ber_header (unsigned char const **buffer, size_t *size,
156                   int *r_class, int *r_tag, 
157                   int *r_constructed, int *r_ndef,
158                   size_t *r_length, size_t *r_nhdr)
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_error (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_error (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_error (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_error (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_error (GPG_ERR_BAD_BER);
212
213       for (; count; count--)
214         {
215           len <<= 8;
216           if (!length)
217             return gpg_error (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 {
261   const unsigned char *s;
262   size_t n, vlen;
263
264   s = *buf;
265   n = *buflen;
266   *tok = NULL;
267   *toklen = 0;
268   if (!n)
269     return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
270   if (*s == '(')
271     {
272       s++; n--;
273       (*depth)++;
274       *buf = s;
275       *buflen = n;
276       return 0;
277     }
278   if (*s == ')')
279     {
280       if (!*depth)
281         return gpg_error (GPG_ERR_INV_SEXP);
282       *toklen = 1;
283       s++; n--;
284       (*depth)--;
285       *buf = s;
286       *buflen = n;
287       return 0;
288     }
289   for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
290     vlen = vlen*10 + (*s - '0');
291   if (!n || *s != ':')
292     return gpg_error (GPG_ERR_INV_SEXP);
293   s++; n--;
294   if (vlen > n)
295     return gpg_error (GPG_ERR_INV_SEXP);
296   *tok = s;
297   *toklen = vlen;
298   s += vlen;
299   n -= vlen;
300   *buf = s;
301   *buflen = n;
302   return 0;
303 }
304