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