1 /* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <openssl/bytestring.h>
16
17 #include <assert.h>
18 #include <string.h>
19
20 #include "internal.h"
21 #include "../internal.h"
22
23
24 // kMaxDepth is a just a sanity limit. The code should be such that the length
25 // of the input being processes always decreases. None the less, a very large
26 // input could otherwise cause the stack to overflow.
27 static const uint32_t kMaxDepth = 2048;
28
29 // is_string_type returns one if |tag| is a string type and zero otherwise. It
30 // ignores the constructed bit.
is_string_type(CBS_ASN1_TAG tag)31 static int is_string_type(CBS_ASN1_TAG tag) {
32 // While BER supports constructed BIT STRINGS, OpenSSL misparses them. To
33 // avoid acting on an ambiguous input, we do not support constructed BIT
34 // STRINGS. See https://github.com/openssl/openssl/issues/12810.
35 switch (tag & ~CBS_ASN1_CONSTRUCTED) {
36 case CBS_ASN1_OCTETSTRING:
37 case CBS_ASN1_UTF8STRING:
38 case CBS_ASN1_NUMERICSTRING:
39 case CBS_ASN1_PRINTABLESTRING:
40 case CBS_ASN1_T61STRING:
41 case CBS_ASN1_VIDEOTEXSTRING:
42 case CBS_ASN1_IA5STRING:
43 case CBS_ASN1_GRAPHICSTRING:
44 case CBS_ASN1_VISIBLESTRING:
45 case CBS_ASN1_GENERALSTRING:
46 case CBS_ASN1_UNIVERSALSTRING:
47 case CBS_ASN1_BMPSTRING:
48 return 1;
49 default:
50 return 0;
51 }
52 }
53
54 // cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found|
55 // depending on whether an indefinite length element or constructed string was
56 // found. The value of |orig_in| is not changed. It returns one on success (i.e.
57 // |*ber_found| was set) and zero on error.
cbs_find_ber(const CBS * orig_in,int * ber_found,uint32_t depth)58 static int cbs_find_ber(const CBS *orig_in, int *ber_found, uint32_t depth) {
59 CBS in;
60
61 if (depth > kMaxDepth) {
62 return 0;
63 }
64
65 CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in));
66 *ber_found = 0;
67
68 while (CBS_len(&in) > 0) {
69 CBS contents;
70 CBS_ASN1_TAG tag;
71 size_t header_len;
72 int indefinite;
73 if (!CBS_get_any_ber_asn1_element(&in, &contents, &tag, &header_len,
74 ber_found, &indefinite)) {
75 return 0;
76 }
77 if (*ber_found) {
78 return 1;
79 }
80 if (tag & CBS_ASN1_CONSTRUCTED) {
81 if (is_string_type(tag)) {
82 // Constructed strings are only legal in BER and require conversion.
83 *ber_found = 1;
84 return 1;
85 }
86 if (!CBS_skip(&contents, header_len) ||
87 !cbs_find_ber(&contents, ber_found, depth + 1)) {
88 return 0;
89 }
90 }
91 }
92
93 return 1;
94 }
95
96 // cbs_get_eoc returns one if |cbs| begins with an "end of contents" (EOC) value
97 // and zero otherwise. If an EOC was found, it advances |cbs| past it.
cbs_get_eoc(CBS * cbs)98 static int cbs_get_eoc(CBS *cbs) {
99 if (CBS_len(cbs) >= 2 &&
100 CBS_data(cbs)[0] == 0 && CBS_data(cbs)[1] == 0) {
101 return CBS_skip(cbs, 2);
102 }
103 return 0;
104 }
105
106 // cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If
107 // |string_tag| is non-zero, then all elements must match |string_tag| up to the
108 // constructed bit and primitive element bodies are written to |out| without
109 // element headers. This is used when concatenating the fragments of a
110 // constructed string. If |looking_for_eoc| is set then any EOC elements found
111 // will cause the function to return after consuming it. It returns one on
112 // success and zero on error.
cbs_convert_ber(CBS * in,CBB * out,CBS_ASN1_TAG string_tag,int looking_for_eoc,uint32_t depth)113 static int cbs_convert_ber(CBS *in, CBB *out, CBS_ASN1_TAG string_tag,
114 int looking_for_eoc, uint32_t depth) {
115 assert(!(string_tag & CBS_ASN1_CONSTRUCTED));
116
117 if (depth > kMaxDepth) {
118 return 0;
119 }
120
121 while (CBS_len(in) > 0) {
122 if (looking_for_eoc && cbs_get_eoc(in)) {
123 return 1;
124 }
125
126 CBS contents;
127 CBS_ASN1_TAG tag, child_string_tag = string_tag;
128 size_t header_len;
129 int indefinite;
130 CBB *out_contents, out_contents_storage;
131 if (!CBS_get_any_ber_asn1_element(in, &contents, &tag, &header_len,
132 /*out_ber_found=*/NULL, &indefinite)) {
133 return 0;
134 }
135
136 if (string_tag != 0) {
137 // This is part of a constructed string. All elements must match
138 // |string_tag| up to the constructed bit and get appended to |out|
139 // without a child element.
140 if ((tag & ~CBS_ASN1_CONSTRUCTED) != string_tag) {
141 return 0;
142 }
143 out_contents = out;
144 } else {
145 CBS_ASN1_TAG out_tag = tag;
146 if ((tag & CBS_ASN1_CONSTRUCTED) && is_string_type(tag)) {
147 // If a constructed string, clear the constructed bit and inform
148 // children to concatenate bodies.
149 out_tag &= ~CBS_ASN1_CONSTRUCTED;
150 child_string_tag = out_tag;
151 }
152 if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) {
153 return 0;
154 }
155 out_contents = &out_contents_storage;
156 }
157
158 if (indefinite) {
159 if (!cbs_convert_ber(in, out_contents, child_string_tag,
160 /*looking_for_eoc=*/1, depth + 1) ||
161 !CBB_flush(out)) {
162 return 0;
163 }
164 continue;
165 }
166
167 if (!CBS_skip(&contents, header_len)) {
168 return 0;
169 }
170
171 if (tag & CBS_ASN1_CONSTRUCTED) {
172 // Recurse into children.
173 if (!cbs_convert_ber(&contents, out_contents, child_string_tag,
174 /*looking_for_eoc=*/0, depth + 1)) {
175 return 0;
176 }
177 } else {
178 // Copy primitive contents as-is.
179 if (!CBB_add_bytes(out_contents, CBS_data(&contents),
180 CBS_len(&contents))) {
181 return 0;
182 }
183 }
184
185 if (!CBB_flush(out)) {
186 return 0;
187 }
188 }
189
190 return looking_for_eoc == 0;
191 }
192
CBS_asn1_ber_to_der(CBS * in,CBS * out,uint8_t ** out_storage)193 int CBS_asn1_ber_to_der(CBS *in, CBS *out, uint8_t **out_storage) {
194 CBB cbb;
195
196 // First, do a quick walk to find any indefinite-length elements. Most of the
197 // time we hope that there aren't any and thus we can quickly return.
198 int conversion_needed;
199 if (!cbs_find_ber(in, &conversion_needed, 0)) {
200 return 0;
201 }
202
203 if (!conversion_needed) {
204 if (!CBS_get_any_asn1_element(in, out, NULL, NULL)) {
205 return 0;
206 }
207 *out_storage = NULL;
208 return 1;
209 }
210
211 size_t len;
212 if (!CBB_init(&cbb, CBS_len(in)) ||
213 !cbs_convert_ber(in, &cbb, 0, 0, 0) ||
214 !CBB_finish(&cbb, out_storage, &len)) {
215 CBB_cleanup(&cbb);
216 return 0;
217 }
218
219 CBS_init(out, *out_storage, len);
220 return 1;
221 }
222
CBS_get_asn1_implicit_string(CBS * in,CBS * out,uint8_t ** out_storage,CBS_ASN1_TAG outer_tag,CBS_ASN1_TAG inner_tag)223 int CBS_get_asn1_implicit_string(CBS *in, CBS *out, uint8_t **out_storage,
224 CBS_ASN1_TAG outer_tag,
225 CBS_ASN1_TAG inner_tag) {
226 assert(!(outer_tag & CBS_ASN1_CONSTRUCTED));
227 assert(!(inner_tag & CBS_ASN1_CONSTRUCTED));
228 assert(is_string_type(inner_tag));
229
230 if (CBS_peek_asn1_tag(in, outer_tag)) {
231 // Normal implicitly-tagged string.
232 *out_storage = NULL;
233 return CBS_get_asn1(in, out, outer_tag);
234 }
235
236 // Otherwise, try to parse an implicitly-tagged constructed string.
237 // |CBS_asn1_ber_to_der| is assumed to have run, so only allow one level deep
238 // of nesting.
239 CBB result;
240 CBS child;
241 if (!CBB_init(&result, CBS_len(in)) ||
242 !CBS_get_asn1(in, &child, outer_tag | CBS_ASN1_CONSTRUCTED)) {
243 goto err;
244 }
245
246 while (CBS_len(&child) > 0) {
247 CBS chunk;
248 if (!CBS_get_asn1(&child, &chunk, inner_tag) ||
249 !CBB_add_bytes(&result, CBS_data(&chunk), CBS_len(&chunk))) {
250 goto err;
251 }
252 }
253
254 uint8_t *data;
255 size_t len;
256 if (!CBB_finish(&result, &data, &len)) {
257 goto err;
258 }
259
260 CBS_init(out, data, len);
261 *out_storage = data;
262 return 1;
263
264 err:
265 CBB_cleanup(&result);
266 return 0;
267 }
268