Updated FSF's address.
[gnupg.git] / scd / 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 2 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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #if GNUPG_MAJOR_VERSION == 1
30 #define GPG_ERR_EOF               (-1)
31 #define GPG_ERR_BAD_BER           (1)  /*G10ERR_GENERAL*/
32 #define GPG_ERR_INV_SEXP          (45) /*G10ERR_INV_ARG*/
33 typedef int gpg_error_t;
34 #define gpg_error(n) (n)
35 #else
36 #include <gpg-error.h>
37 #endif
38
39 #include "tlv.h"
40
41 static const unsigned char *
42 do_find_tlv (const unsigned char *buffer, size_t length,
43              int tag, size_t *nbytes, int nestlevel)
44 {
45   const unsigned char *s = buffer;
46   size_t n = length;
47   size_t len;
48   int this_tag;
49   int composite;
50     
51   for (;;)
52     {
53       buffer = s;
54       if (n < 2)
55         return NULL; /* Buffer definitely too short for tag and length. */
56       if (!*s || *s == 0xff)
57         { /* Skip optional filler between TLV objects. */
58           s++;
59           n--;
60           continue;
61         }
62       composite = !!(*s & 0x20);
63       if ((*s & 0x1f) == 0x1f)
64         { /* more tag bytes to follow */
65           s++;
66           n--;
67           if (n < 2)
68             return NULL; /* buffer definitely too short for tag and length. */
69           if ((*s & 0x1f) == 0x1f)
70             return NULL; /* We support only up to 2 bytes. */
71           this_tag = (s[-1] << 8) | (s[0] & 0x7f);
72         }
73       else
74         this_tag = s[0];
75       len = s[1];
76       s += 2; n -= 2;
77       if (len < 0x80)
78         ;
79       else if (len == 0x81)
80         { /* One byte length follows. */
81           if (!n)
82             return NULL; /* we expected 1 more bytes with the length. */
83           len = s[0];
84           s++; n--;
85         }
86       else if (len == 0x82)
87         { /* Two byte length follows. */
88           if (n < 2)
89             return NULL; /* We expected 2 more bytes with the length. */
90           len = (s[0] << 8) | s[1];
91           s += 2; n -= 2;
92         }
93       else
94         return NULL; /* APDU limit is 65535, thus it does not make
95                         sense to assume longer length fields. */
96
97       if (composite && nestlevel < 100)
98         { /* Dive into this composite DO after checking for a too deep
99              nesting. */
100           const unsigned char *tmp_s;
101           size_t tmp_len;
102           
103           tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
104           if (tmp_s)
105             {
106               *nbytes = tmp_len;
107               return tmp_s;
108             }
109         }
110
111       if (this_tag == tag)
112         {
113           *nbytes = len;
114           return s;
115         }
116       if (len > n)
117         return NULL; /* Buffer too short to skip to the next tag. */
118       s += len; n -= len;
119     }
120 }
121
122
123 /* Locate a TLV encoded data object in BUFFER of LENGTH and
124    return a pointer to value as well as its length in NBYTES.  Return
125    NULL if it was not found or if the object does not fit into the buffer. */
126 const unsigned char *
127 find_tlv (const unsigned char *buffer, size_t length,
128           int tag, size_t *nbytes)
129 {
130   const unsigned char *p;
131
132   p = do_find_tlv (buffer, length, tag, nbytes, 0);
133   if (p && *nbytes > (length - (p-buffer)))
134     p = NULL; /* Object longer than buffer. */
135   return p;
136 }
137
138
139
140 /* Locate a TLV encoded data object in BUFFER of LENGTH and
141    return a pointer to value as well as its length in NBYTES.  Return
142    NULL if it was not found.  Note, that the function does not check
143    whether the value fits into the provided buffer. */
144 const unsigned char *
145 find_tlv_unchecked (const unsigned char *buffer, size_t length,
146                     int tag, size_t *nbytes)
147 {
148   return do_find_tlv (buffer, length, tag, nbytes, 0);
149 }
150
151
152 /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
153    and the length part from the TLV triplet.  Update BUFFER and SIZE
154    on success. */
155 gpg_error_t
156 parse_ber_header (unsigned char const **buffer, size_t *size,
157                   int *r_class, int *r_tag, 
158                   int *r_constructed, int *r_ndef,
159                   size_t *r_length, size_t *r_nhdr)
160 {
161   int c;
162   unsigned long tag;
163   const unsigned char *buf = *buffer;
164   size_t length = *size;
165
166   *r_ndef = 0;
167   *r_length = 0;
168   *r_nhdr = 0;
169
170   /* Get the tag. */
171   if (!length)
172     return gpg_error (GPG_ERR_EOF);
173   c = *buf++; length--; ++*r_nhdr;
174
175   *r_class = (c & 0xc0) >> 6;
176   *r_constructed = !!(c & 0x20);
177   tag = c & 0x1f;
178
179   if (tag == 0x1f)
180     {
181       tag = 0;
182       do
183         {
184           tag <<= 7;
185           if (!length)
186             return gpg_error (GPG_ERR_EOF);
187           c = *buf++; length--; ++*r_nhdr;
188           tag |= c & 0x7f;
189
190         }
191       while (c & 0x80);
192     }
193   *r_tag = tag;
194
195   /* Get the length. */
196   if (!length)
197     return gpg_error (GPG_ERR_EOF);
198   c = *buf++; length--; ++*r_nhdr;
199
200   if ( !(c & 0x80) )
201     *r_length = c;
202   else if (c == 0x80)
203     *r_ndef = 1;
204   else if (c == 0xff)
205     return gpg_error (GPG_ERR_BAD_BER);
206   else
207     {
208       unsigned long len = 0;
209       int count = c & 0x7f;
210
211       if (count > sizeof (len) || count > sizeof (size_t))
212         return gpg_error (GPG_ERR_BAD_BER);
213
214       for (; count; count--)
215         {
216           len <<= 8;
217           if (!length)
218             return gpg_error (GPG_ERR_EOF);
219           c = *buf++; length--; ++*r_nhdr;
220           len |= c & 0xff;
221         }
222       *r_length = len;
223     }
224   
225   /* Without this kludge some example certs can't be parsed. */
226   if (*r_class == CLASS_UNIVERSAL && !*r_tag)
227     *r_length = 0;
228   
229   *buffer = buf;
230   *size = length;
231   return 0;
232 }
233
234
235 /* FIXME: The following function should not go into this file but for
236    now it is easier to keep it here. */
237
238 /* Return the next token of an canconical encoded S-expression.  BUF
239    is the pointer to the S-expression and BUFLEN is a pointer to the
240    length of this S-expression (used to validate the syntax).  Both
241    are updated to reflect the new position.  The token itself is
242    returned as a pointer into the orginal buffer at TOK and TOKLEN.
243    If a parentheses is the next token, TOK will be set to NULL.
244    TOKLEN is checked to be within the bounds.  On error a error code
245    is returned and all pointers should are not guaranteed to point to
246    a meanigful value. DEPTH should be initialized to 0 and will
247    reflect on return the actual depth of the tree. To detect the end
248    of the S-expression it is advisable to check DEPTH after a
249    successful return:
250
251    depth = 0;
252    while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
253           && depth)
254      process_token (tok, toklen);
255    if (err)  
256      handle_error ();
257  */
258 gpg_error_t
259 parse_sexp (unsigned char const **buf, size_t *buflen,
260             int *depth, unsigned char const **tok, size_t *toklen)
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_error (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_error (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_error (GPG_ERR_INV_SEXP);
294   s++; n--;
295   if (vlen > n)
296     return gpg_error (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