Changed to GPLv3.
[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 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 #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 {
159   int c;
160   unsigned long tag;
161   const unsigned char *buf = *buffer;
162   size_t length = *size;
163
164   *r_ndef = 0;
165   *r_length = 0;
166   *r_nhdr = 0;
167
168   /* Get the tag. */
169   if (!length)
170     return gpg_error (GPG_ERR_EOF);
171   c = *buf++; length--; ++*r_nhdr;
172
173   *r_class = (c & 0xc0) >> 6;
174   *r_constructed = !!(c & 0x20);
175   tag = c & 0x1f;
176
177   if (tag == 0x1f)
178     {
179       tag = 0;
180       do
181         {
182           tag <<= 7;
183           if (!length)
184             return gpg_error (GPG_ERR_EOF);
185           c = *buf++; length--; ++*r_nhdr;
186           tag |= c & 0x7f;
187
188         }
189       while (c & 0x80);
190     }
191   *r_tag = tag;
192
193   /* Get the length. */
194   if (!length)
195     return gpg_error (GPG_ERR_EOF);
196   c = *buf++; length--; ++*r_nhdr;
197
198   if ( !(c & 0x80) )
199     *r_length = c;
200   else if (c == 0x80)
201     *r_ndef = 1;
202   else if (c == 0xff)
203     return gpg_error (GPG_ERR_BAD_BER);
204   else
205     {
206       unsigned long len = 0;
207       int count = c & 0x7f;
208
209       if (count > sizeof (len) || count > sizeof (size_t))
210         return gpg_error (GPG_ERR_BAD_BER);
211
212       for (; count; count--)
213         {
214           len <<= 8;
215           if (!length)
216             return gpg_error (GPG_ERR_EOF);
217           c = *buf++; length--; ++*r_nhdr;
218           len |= c & 0xff;
219         }
220       *r_length = len;
221     }
222   
223   /* Without this kludge some example certs can't be parsed. */
224   if (*r_class == CLASS_UNIVERSAL && !*r_tag)
225     *r_length = 0;
226   
227   *buffer = buf;
228   *size = length;
229   return 0;
230 }
231
232
233 /* FIXME: The following function should not go into this file but for
234    now it is easier to keep it here. */
235
236 /* Return the next token of an canconical encoded S-expression.  BUF
237    is the pointer to the S-expression and BUFLEN is a pointer to the
238    length of this S-expression (used to validate the syntax).  Both
239    are updated to reflect the new position.  The token itself is
240    returned as a pointer into the orginal buffer at TOK and TOKLEN.
241    If a parentheses is the next token, TOK will be set to NULL.
242    TOKLEN is checked to be within the bounds.  On error a error code
243    is returned and all pointers should are not guaranteed to point to
244    a meanigful value. DEPTH should be initialized to 0 and will
245    reflect on return the actual depth of the tree. To detect the end
246    of the S-expression it is advisable to check DEPTH after a
247    successful return:
248
249    depth = 0;
250    while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
251           && depth)
252      process_token (tok, toklen);
253    if (err)  
254      handle_error ();
255  */
256 gpg_error_t
257 parse_sexp (unsigned char const **buf, size_t *buflen,
258             int *depth, unsigned char const **tok, size_t *toklen)
259 {
260   const unsigned char *s;
261   size_t n, vlen;
262
263   s = *buf;
264   n = *buflen;
265   *tok = NULL;
266   *toklen = 0;
267   if (!n)
268     return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
269   if (*s == '(')
270     {
271       s++; n--;
272       (*depth)++;
273       *buf = s;
274       *buflen = n;
275       return 0;
276     }
277   if (*s == ')')
278     {
279       if (!*depth)
280         return gpg_error (GPG_ERR_INV_SEXP);
281       *toklen = 1;
282       s++; n--;
283       (*depth)--;
284       *buf = s;
285       *buflen = n;
286       return 0;
287     }
288   for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
289     vlen = vlen*10 + (*s - '0');
290   if (!n || *s != ':')
291     return gpg_error (GPG_ERR_INV_SEXP);
292   s++; n--;
293   if (vlen > n)
294     return gpg_error (GPG_ERR_INV_SEXP);
295   *tok = s;
296   *toklen = vlen;
297   s += vlen;
298   n -= vlen;
299   *buf = s;
300   *buflen = n;
301   return 0;
302 }
303