diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/libssl/bs_ber.c | 270 |
1 files changed, 0 insertions, 270 deletions
diff --git a/src/lib/libssl/bs_ber.c b/src/lib/libssl/bs_ber.c deleted file mode 100644 index 923ec06f3d..0000000000 --- a/src/lib/libssl/bs_ber.c +++ /dev/null | |||
@@ -1,270 +0,0 @@ | |||
1 | /* $OpenBSD: bs_ber.c,v 1.13 2025/03/28 12:13:03 tb Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2014, Google Inc. | ||
4 | * | ||
5 | * Permission to use, copy, modify, and/or 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 ANY | ||
12 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
14 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
15 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #include <stdint.h> | ||
19 | #include <string.h> | ||
20 | |||
21 | #include "bytestring.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 | */ | ||
28 | static const unsigned int kMaxDepth = 2048; | ||
29 | |||
30 | /* Non-strict version that allows a relaxed DER with indefinite form. */ | ||
31 | static int | ||
32 | cbs_nonstrict_get_any_asn1_element(CBS *cbs, CBS *out, unsigned int *out_tag, | ||
33 | size_t *out_header_len) | ||
34 | { | ||
35 | return cbs_get_any_asn1_element_internal(cbs, out, | ||
36 | out_tag, out_header_len, 0); | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * cbs_find_indefinite walks an ASN.1 structure in |orig_in| and sets | ||
41 | * |*indefinite_found| depending on whether an indefinite length element was | ||
42 | * found. The value of |orig_in| is not modified. | ||
43 | * | ||
44 | * Returns one on success (i.e. |*indefinite_found| was set) and zero on error. | ||
45 | */ | ||
46 | static int | ||
47 | cbs_find_indefinite(const CBS *orig_in, char *indefinite_found, | ||
48 | unsigned int depth) | ||
49 | { | ||
50 | CBS in; | ||
51 | |||
52 | if (depth > kMaxDepth) | ||
53 | return 0; | ||
54 | |||
55 | CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in)); | ||
56 | |||
57 | while (CBS_len(&in) > 0) { | ||
58 | CBS contents; | ||
59 | unsigned int tag; | ||
60 | size_t header_len; | ||
61 | |||
62 | if (!cbs_nonstrict_get_any_asn1_element(&in, &contents, &tag, | ||
63 | &header_len)) | ||
64 | return 0; | ||
65 | |||
66 | /* Indefinite form not allowed by DER. */ | ||
67 | if (CBS_len(&contents) == header_len && header_len > 0 && | ||
68 | CBS_data(&contents)[header_len - 1] == 0x80) { | ||
69 | *indefinite_found = 1; | ||
70 | return 1; | ||
71 | } | ||
72 | if (tag & CBS_ASN1_CONSTRUCTED) { | ||
73 | if (!CBS_skip(&contents, header_len) || | ||
74 | !cbs_find_indefinite(&contents, indefinite_found, | ||
75 | depth + 1)) | ||
76 | return 0; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | *indefinite_found = 0; | ||
81 | return 1; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * is_primitive_type returns true if |tag| likely a primitive type. Normally | ||
86 | * one can just test the "constructed" bit in the tag but, in BER, even | ||
87 | * primitive tags can have the constructed bit if they have indefinite | ||
88 | * length. | ||
89 | */ | ||
90 | static char | ||
91 | is_primitive_type(unsigned int tag) | ||
92 | { | ||
93 | return (tag & 0xc0) == 0 && | ||
94 | (tag & 0x1f) != (CBS_ASN1_SEQUENCE & 0x1f) && | ||
95 | (tag & 0x1f) != (CBS_ASN1_SET & 0x1f); | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * is_eoc returns true if |header_len| and |contents|, as returned by | ||
100 | * |cbs_nonstrict_get_any_asn1_element|, indicate an "end of contents" (EOC) | ||
101 | * value. | ||
102 | */ | ||
103 | static char | ||
104 | is_eoc(size_t header_len, CBS *contents) | ||
105 | { | ||
106 | const unsigned char eoc[] = {0x0, 0x0}; | ||
107 | |||
108 | return header_len == 2 && CBS_mem_equal(contents, eoc, 2); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * cbs_convert_indefinite reads data with DER encoding (but relaxed to allow | ||
113 | * indefinite form) from |in| and writes definite form DER data to |out|. If | ||
114 | * |squash_header| is set then the top-level of elements from |in| will not | ||
115 | * have their headers written. This is used when concatenating the fragments of | ||
116 | * an indefinite length, primitive value. If |looking_for_eoc| is set then any | ||
117 | * EOC elements found will cause the function to return after consuming it. | ||
118 | * It returns one on success and zero on error. | ||
119 | */ | ||
120 | static int | ||
121 | cbs_convert_indefinite(CBS *in, CBB *out, char squash_header, | ||
122 | char looking_for_eoc, unsigned int depth) | ||
123 | { | ||
124 | if (depth > kMaxDepth) | ||
125 | return 0; | ||
126 | |||
127 | while (CBS_len(in) > 0) { | ||
128 | CBS contents; | ||
129 | unsigned int tag; | ||
130 | size_t header_len; | ||
131 | CBB *out_contents, out_contents_storage; | ||
132 | |||
133 | if (!cbs_nonstrict_get_any_asn1_element(in, &contents, &tag, | ||
134 | &header_len)) | ||
135 | return 0; | ||
136 | |||
137 | out_contents = out; | ||
138 | |||
139 | if (CBS_len(&contents) == header_len) { | ||
140 | if (is_eoc(header_len, &contents)) | ||
141 | return looking_for_eoc; | ||
142 | |||
143 | if (header_len > 0 && | ||
144 | CBS_data(&contents)[header_len - 1] == 0x80) { | ||
145 | /* | ||
146 | * This is an indefinite length element. If | ||
147 | * it's a SEQUENCE or SET then we just need to | ||
148 | * write the out the contents as normal, but | ||
149 | * with a concrete length prefix. | ||
150 | * | ||
151 | * If it's a something else then the contents | ||
152 | * will be a series of DER elements of the same | ||
153 | * type which need to be concatenated. | ||
154 | */ | ||
155 | const char context_specific = (tag & 0xc0) | ||
156 | == 0x80; | ||
157 | char squash_child_headers = | ||
158 | is_primitive_type(tag); | ||
159 | |||
160 | /* | ||
161 | * This is a hack, but it sufficies to handle | ||
162 | * NSS's output. If we find an indefinite | ||
163 | * length, context-specific tag with a definite, | ||
164 | * primitive tag inside it, then we assume that | ||
165 | * the context-specific tag is implicit and the | ||
166 | * tags within are fragments of a primitive type | ||
167 | * that need to be concatenated. | ||
168 | */ | ||
169 | if (context_specific && | ||
170 | (tag & CBS_ASN1_CONSTRUCTED)) { | ||
171 | CBS in_copy, inner_contents; | ||
172 | unsigned int inner_tag; | ||
173 | size_t inner_header_len; | ||
174 | |||
175 | CBS_init(&in_copy, CBS_data(in), | ||
176 | CBS_len(in)); | ||
177 | if (!cbs_nonstrict_get_any_asn1_element( | ||
178 | &in_copy, &inner_contents, | ||
179 | &inner_tag, &inner_header_len)) | ||
180 | return 0; | ||
181 | |||
182 | if (CBS_len(&inner_contents) > | ||
183 | inner_header_len && | ||
184 | is_primitive_type(inner_tag)) | ||
185 | squash_child_headers = 1; | ||
186 | } | ||
187 | |||
188 | if (!squash_header) { | ||
189 | unsigned int out_tag = tag; | ||
190 | |||
191 | if (squash_child_headers) | ||
192 | out_tag &= | ||
193 | ~CBS_ASN1_CONSTRUCTED; | ||
194 | |||
195 | if (!CBB_add_asn1(out, | ||
196 | &out_contents_storage, out_tag)) | ||
197 | return 0; | ||
198 | |||
199 | out_contents = &out_contents_storage; | ||
200 | } | ||
201 | |||
202 | if (!cbs_convert_indefinite(in, out_contents, | ||
203 | squash_child_headers, | ||
204 | 1 /* looking for eoc */, depth + 1)) | ||
205 | return 0; | ||
206 | |||
207 | if (out_contents != out && !CBB_flush(out)) | ||
208 | return 0; | ||
209 | |||
210 | continue; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | if (!squash_header) { | ||
215 | if (!CBB_add_asn1(out, &out_contents_storage, tag)) | ||
216 | return 0; | ||
217 | |||
218 | out_contents = &out_contents_storage; | ||
219 | } | ||
220 | |||
221 | if (!CBS_skip(&contents, header_len)) | ||
222 | return 0; | ||
223 | |||
224 | if (tag & CBS_ASN1_CONSTRUCTED) { | ||
225 | if (!cbs_convert_indefinite(&contents, out_contents, | ||
226 | 0 /* don't squash header */, | ||
227 | 0 /* not looking for eoc */, depth + 1)) | ||
228 | return 0; | ||
229 | } else { | ||
230 | if (!CBB_add_bytes(out_contents, CBS_data(&contents), | ||
231 | CBS_len(&contents))) | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | if (out_contents != out && !CBB_flush(out)) | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | return looking_for_eoc == 0; | ||
240 | } | ||
241 | |||
242 | int | ||
243 | CBS_asn1_indefinite_to_definite(CBS *in, uint8_t **out, size_t *out_len) | ||
244 | { | ||
245 | CBB cbb; | ||
246 | |||
247 | /* | ||
248 | * First, do a quick walk to find any indefinite-length elements. Most | ||
249 | * of the time we hope that there aren't any and thus we can quickly | ||
250 | * return. | ||
251 | */ | ||
252 | char conversion_needed; | ||
253 | if (!cbs_find_indefinite(in, &conversion_needed, 0)) | ||
254 | return 0; | ||
255 | |||
256 | if (!conversion_needed) { | ||
257 | *out = NULL; | ||
258 | *out_len = 0; | ||
259 | return 1; | ||
260 | } | ||
261 | |||
262 | if (!CBB_init(&cbb, CBS_len(in))) | ||
263 | return 0; | ||
264 | if (!cbs_convert_indefinite(in, &cbb, 0, 0, 0)) { | ||
265 | CBB_cleanup(&cbb); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | return CBB_finish(&cbb, out, out_len); | ||
270 | } | ||