Coverage Report

Created: 2022-08-24 06:30

/src/libressl/crypto/asn1/asn1_lib.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: asn1_lib.c,v 1.54 2022/05/05 19:18:56 jsing Exp $ */
2
/*
3
 * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <limits.h>
19
#include <stdlib.h>
20
21
#include "bytestring.h"
22
23
int
24
asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class,
25
    int *out_constructed, uint32_t *out_tag_number)
26
0
{
27
0
  uint8_t tag_class, tag_val;
28
0
  int tag_constructed;
29
0
  uint32_t tag_number;
30
31
  /*
32
   * Decode ASN.1 identifier octets - see ITU-T X.690 section 8.1.2.
33
   */
34
35
0
  *out_class = 0;
36
0
  *out_constructed = 0;
37
0
  *out_tag_number = 0;
38
39
0
  if (!CBS_get_u8(cbs, &tag_val))
40
0
    return 0;
41
42
  /*
43
   * ASN.1 tag class, encoding (primitive or constructed) and tag number
44
   * are encoded in one or more identifier octets - the first octet
45
   * contains the 2 bit tag class, the 1 bit encoding type and 5 bits
46
   * of tag number.
47
   *
48
   * For tag numbers larger than 30 (0x1e) the 5 bit tag number in the
49
   * first octet is set to all ones (0x1f) - the tag number is then
50
   * encoded in subsequent octets - each of which have a one bit
51
   * continuation flag and 7 bits of tag number in big-endian form.
52
   * The encoding should not contain leading zeros but can for BER.
53
   */
54
0
  tag_class = (tag_val >> 6) & 0x3;
55
0
  tag_constructed = (tag_val >> 5) & 0x1;
56
0
  tag_number = tag_val & 0x1f;
57
58
  /* Long form. */
59
0
  if (tag_number == 0x1f) {
60
0
    tag_number = 0;
61
0
    do {
62
0
      if (!CBS_get_u8(cbs, &tag_val))
63
0
        return 0;
64
0
      if (der_mode && tag_number == 0 && tag_val == 0x80)
65
0
        return 0;
66
0
      if (tag_number > (UINT32_MAX >> 7))
67
0
        return 0;
68
0
      tag_number = tag_number << 7 | (tag_val & 0x7f);
69
0
    } while ((tag_val & 0x80) != 0);
70
0
  }
71
72
0
  *out_class = tag_class;
73
0
  *out_constructed = tag_constructed;
74
0
  *out_tag_number = tag_number;
75
76
0
  return 1;
77
0
}
78
79
int
80
asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite,
81
    size_t *out_length)
82
0
{
83
0
  uint8_t len_bytes;
84
0
  size_t length;
85
0
  uint8_t val;
86
87
  /*
88
   * Decode ASN.1 length octets - see ITU-T X.690 section 8.1.3.
89
   */
90
91
0
  *out_length = 0;
92
0
  *out_indefinite = 0;
93
94
0
  if (!CBS_get_u8(cbs, &val))
95
0
    return 0;
96
97
  /*
98
   * Short form - length is encoded in the lower 7 bits of a single byte.
99
   */
100
0
  if (val < 0x80) {
101
0
    *out_length = val;
102
0
    return 1;
103
0
  }
104
105
  /*
106
   * Indefinite length - content continues until an End of Content (EOC)
107
   * marker is reached. Must be used with constructed encoding.
108
   */
109
0
  if (val == 0x80) {
110
0
    *out_indefinite = 1;
111
0
    return 1;
112
0
  }
113
114
  /*
115
   * Long form - the lower 7 bits of the first byte specifies the number
116
   * of bytes used to encode the length, the following bytes specify the
117
   * length in big-endian form. The encoding should not contain leading
118
   * zeros but can for BER. A length value of 0x7f is invalid.
119
   */
120
0
  if ((len_bytes = val & 0x7f) == 0x7f)
121
0
    return 0;
122
123
0
  length = 0;
124
125
0
  while (len_bytes-- > 0) {
126
0
    if (!CBS_get_u8(cbs, &val))
127
0
      return 0;
128
0
    if (der_mode && length == 0 && val == 0)
129
0
      return 0;
130
0
    if (length > (SIZE_MAX >> 8))
131
0
      return 0;
132
0
    length = (length << 8) | val;
133
0
  }
134
135
0
  *out_length = length;
136
137
0
  return 1;
138
0
}
139
140
int
141
asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_tag_class,
142
    int *out_constructed, uint32_t *out_tag_number, int *out_indefinite,
143
    size_t *out_length)
144
0
{
145
0
  int constructed, indefinite;
146
0
  uint32_t tag_number;
147
0
  uint8_t tag_class;
148
0
  size_t length;
149
150
0
  *out_tag_class = 0;
151
0
  *out_constructed = 0;
152
0
  *out_tag_number = 0;
153
0
  *out_indefinite = 0;
154
0
  *out_length = 0;
155
156
0
  if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
157
0
      &tag_number))
158
0
    return 0;
159
0
  if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
160
0
    return 0;
161
162
  /* Indefinite length can only be used with constructed encoding. */
163
0
  if (indefinite && !constructed)
164
0
    return 0;
165
166
0
  *out_tag_class = tag_class;
167
0
  *out_constructed = constructed;
168
0
  *out_tag_number = tag_number;
169
0
  *out_indefinite = indefinite;
170
0
  *out_length = length;
171
172
0
  return 1;
173
0
}
174
175
int
176
asn1_get_primitive(CBS *cbs, int der_mode, uint32_t *out_tag_number,
177
    CBS *out_content)
178
0
{
179
0
  int constructed, indefinite;
180
0
  uint32_t tag_number;
181
0
  uint8_t tag_class;
182
0
  size_t length;
183
184
0
  *out_tag_number = 0;
185
186
0
  CBS_init(out_content, NULL, 0);
187
188
0
  if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
189
0
      &tag_number))
190
0
    return 0;
191
0
  if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
192
0
    return 0;
193
194
  /* A primitive is not constructed and has a definite length. */
195
0
  if (constructed || indefinite)
196
0
    return 0;
197
198
0
  if (!CBS_get_bytes(cbs, out_content, length))
199
0
    return 0;
200
201
0
  *out_tag_number = tag_number;
202
203
0
  return 1;
204
0
}