~ubuntu-branches/ubuntu/precise/gnupg2/precise-proposed

« back to all changes in this revision

Viewing changes to scd/tlv.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Mueller
  • Date: 2005-03-29 10:30:32 UTC
  • Revision ID: james.westby@ubuntu.com-20050329103032-sj42n2ain3ipx310
Tags: upstream-1.9.15
ImportĀ upstreamĀ versionĀ 1.9.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* tlv.c - Tag-Length-Value Utilities
 
2
 *      Copyright (C) 2003, 2004 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.  Note, that the function does not check
 
117
   whether the value fits into the provided buffer. */
 
118
const unsigned char *
 
119
find_tlv (const unsigned char *buffer, size_t length,
 
120
          int tag, size_t *nbytes)
 
121
{
 
122
  return do_find_tlv (buffer, length, tag, nbytes, 0);
 
123
}
 
124
 
 
125
 
 
126
 
 
127
 
 
128
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
 
129
   and the length part from the TLV triplet.  Update BUFFER and SIZE
 
130
   on success. */
 
131
gpg_error_t
 
132
parse_ber_header (unsigned char const **buffer, size_t *size,
 
133
                  int *r_class, int *r_tag, 
 
134
                  int *r_constructed, int *r_ndef,
 
135
                  size_t *r_length, size_t *r_nhdr)
 
136
{
 
137
  int c;
 
138
  unsigned long tag;
 
139
  const unsigned char *buf = *buffer;
 
140
  size_t length = *size;
 
141
 
 
142
  *r_ndef = 0;
 
143
  *r_length = 0;
 
144
  *r_nhdr = 0;
 
145
 
 
146
  /* Get the tag. */
 
147
  if (!length)
 
148
    return gpg_error (GPG_ERR_EOF);
 
149
  c = *buf++; length--; ++*r_nhdr;
 
150
 
 
151
  *r_class = (c & 0xc0) >> 6;
 
152
  *r_constructed = !!(c & 0x20);
 
153
  tag = c & 0x1f;
 
154
 
 
155
  if (tag == 0x1f)
 
156
    {
 
157
      tag = 0;
 
158
      do
 
159
        {
 
160
          tag <<= 7;
 
161
          if (!length)
 
162
            return gpg_error (GPG_ERR_EOF);
 
163
          c = *buf++; length--; ++*r_nhdr;
 
164
          tag |= c & 0x7f;
 
165
 
 
166
        }
 
167
      while (c & 0x80);
 
168
    }
 
169
  *r_tag = tag;
 
170
 
 
171
  /* Get the length. */
 
172
  if (!length)
 
173
    return gpg_error (GPG_ERR_EOF);
 
174
  c = *buf++; length--; ++*r_nhdr;
 
175
 
 
176
  if ( !(c & 0x80) )
 
177
    *r_length = c;
 
178
  else if (c == 0x80)
 
179
    *r_ndef = 1;
 
180
  else if (c == 0xff)
 
181
    return gpg_error (GPG_ERR_BAD_BER);
 
182
  else
 
183
    {
 
184
      unsigned long len = 0;
 
185
      int count = c & 0x7f;
 
186
 
 
187
      if (count > sizeof (len) || count > sizeof (size_t))
 
188
        return gpg_error (GPG_ERR_BAD_BER);
 
189
 
 
190
      for (; count; count--)
 
191
        {
 
192
          len <<= 8;
 
193
          if (!length)
 
194
            return gpg_error (GPG_ERR_EOF);
 
195
          c = *buf++; length--; ++*r_nhdr;
 
196
          len |= c & 0xff;
 
197
        }
 
198
      *r_length = len;
 
199
    }
 
200
  
 
201
  /* Without this kludge some example certs can't be parsed. */
 
202
  if (*r_class == CLASS_UNIVERSAL && !*r_tag)
 
203
    *r_length = 0;
 
204
  
 
205
  *buffer = buf;
 
206
  *size = length;
 
207
  return 0;
 
208
}