* app-p15.c (micardo_mse): New.
[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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include <gpg-error.h>
29
30 #include "tlv.h"
31
32 static const unsigned char *
33 do_find_tlv (const unsigned char *buffer, size_t length,
34              int tag, size_t *nbytes, int nestlevel)
35 {
36   const unsigned char *s = buffer;
37   size_t n = length;
38   size_t len;
39   int this_tag;
40   int composite;
41     
42   for (;;)
43     {
44       buffer = s;
45       if (n < 2)
46         return NULL; /* Buffer definitely too short for tag and length. */
47       if (!*s || *s == 0xff)
48         { /* Skip optional filler between TLV objects. */
49           s++;
50           n--;
51           continue;
52         }
53       composite = !!(*s & 0x20);
54       if ((*s & 0x1f) == 0x1f)
55         { /* more tag bytes to follow */
56           s++;
57           n--;
58           if (n < 2)
59             return NULL; /* buffer definitely too short for tag and length. */
60           if ((*s & 0x1f) == 0x1f)
61             return NULL; /* We support only up to 2 bytes. */
62           this_tag = (s[-1] << 8) | (s[0] & 0x7f);
63         }
64       else
65         this_tag = s[0];
66       len = s[1];
67       s += 2; n -= 2;
68       if (len < 0x80)
69         ;
70       else if (len == 0x81)
71         { /* One byte length follows. */
72           if (!n)
73             return NULL; /* we expected 1 more bytes with the length. */
74           len = s[0];
75           s++; n--;
76         }
77       else if (len == 0x82)
78         { /* Two byte length follows. */
79           if (n < 2)
80             return NULL; /* We expected 2 more bytes with the length. */
81           len = (s[0] << 8) | s[1];
82           s += 2; n -= 2;
83         }
84       else
85         return NULL; /* APDU limit is 65535, thus it does not make
86                         sense to assume longer length fields. */
87
88       if (composite && nestlevel < 100)
89         { /* Dive into this composite DO after checking for a too deep
90              nesting. */
91           const unsigned char *tmp_s;
92           size_t tmp_len;
93           
94           tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
95           if (tmp_s)
96             {
97               *nbytes = tmp_len;
98               return tmp_s;
99             }
100         }
101
102       if (this_tag == tag)
103         {
104           *nbytes = len;
105           return s;
106         }
107       if (len > n)
108         return NULL; /* Buffer too short to skip to the next tag. */
109       s += len; n -= len;
110     }
111 }
112
113
114 /* Locate a TLV encoded data object in BUFFER of LENGTH and
115    return a pointer to value as well as its length in NBYTES.  Return
116    NULL if it was not found or if the object does not fit into the buffer. */
117 const unsigned char *
118 find_tlv (const unsigned char *buffer, size_t length,
119           int tag, size_t *nbytes)
120 {
121   const unsigned char *p;
122
123   p = do_find_tlv (buffer, length, tag, nbytes, 0);
124   if (p && *nbytes > (length - (p-buffer)))
125     p = NULL; /* Object longer than buffer. */
126   return p;
127 }
128
129
130
131 /* Locate a TLV encoded data object in BUFFER of LENGTH and
132    return a pointer to value as well as its length in NBYTES.  Return
133    NULL if it was not found.  Note, that the function does not check
134    whether the value fits into the provided buffer. */
135 const unsigned char *
136 find_tlv_unchecked (const unsigned char *buffer, size_t length,
137                     int tag, size_t *nbytes)
138 {
139   return do_find_tlv (buffer, length, tag, nbytes, 0);
140 }
141
142
143 /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
144    and the length part from the TLV triplet.  Update BUFFER and SIZE
145    on success. */
146 gpg_error_t
147 parse_ber_header (unsigned char const **buffer, size_t *size,
148                   int *r_class, int *r_tag, 
149                   int *r_constructed, int *r_ndef,
150                   size_t *r_length, size_t *r_nhdr)
151 {
152   int c;
153   unsigned long tag;
154   const unsigned char *buf = *buffer;
155   size_t length = *size;
156
157   *r_ndef = 0;
158   *r_length = 0;
159   *r_nhdr = 0;
160
161   /* Get the tag. */
162   if (!length)
163     return gpg_error (GPG_ERR_EOF);
164   c = *buf++; length--; ++*r_nhdr;
165
166   *r_class = (c & 0xc0) >> 6;
167   *r_constructed = !!(c & 0x20);
168   tag = c & 0x1f;
169
170   if (tag == 0x1f)
171     {
172       tag = 0;
173       do
174         {
175           tag <<= 7;
176           if (!length)
177             return gpg_error (GPG_ERR_EOF);
178           c = *buf++; length--; ++*r_nhdr;
179           tag |= c & 0x7f;
180
181         }
182       while (c & 0x80);
183     }
184   *r_tag = tag;
185
186   /* Get the length. */
187   if (!length)
188     return gpg_error (GPG_ERR_EOF);
189   c = *buf++; length--; ++*r_nhdr;
190
191   if ( !(c & 0x80) )
192     *r_length = c;
193   else if (c == 0x80)
194     *r_ndef = 1;
195   else if (c == 0xff)
196     return gpg_error (GPG_ERR_BAD_BER);
197   else
198     {
199       unsigned long len = 0;
200       int count = c & 0x7f;
201
202       if (count > sizeof (len) || count > sizeof (size_t))
203         return gpg_error (GPG_ERR_BAD_BER);
204
205       for (; count; count--)
206         {
207           len <<= 8;
208           if (!length)
209             return gpg_error (GPG_ERR_EOF);
210           c = *buf++; length--; ++*r_nhdr;
211           len |= c & 0xff;
212         }
213       *r_length = len;
214     }
215   
216   /* Without this kludge some example certs can't be parsed. */
217   if (*r_class == CLASS_UNIVERSAL && !*r_tag)
218     *r_length = 0;
219   
220   *buffer = buf;
221   *size = length;
222   return 0;
223 }