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