1*c57a2cdeStb /* $OpenBSD: bs_ber.c,v 1.12 2024/05/25 15:14:26 tb Exp $ */
2c4905cd3Sdoug /*
3c4905cd3Sdoug * Copyright (c) 2014, Google Inc.
4c4905cd3Sdoug *
5c4905cd3Sdoug * Permission to use, copy, modify, and/or distribute this software for any
6c4905cd3Sdoug * purpose with or without fee is hereby granted, provided that the above
7c4905cd3Sdoug * copyright notice and this permission notice appear in all copies.
8c4905cd3Sdoug *
9c4905cd3Sdoug * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10c4905cd3Sdoug * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11c4905cd3Sdoug * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12c4905cd3Sdoug * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13c4905cd3Sdoug * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14c4905cd3Sdoug * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15d2f1cbaeSjsing * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d2f1cbaeSjsing */
17c4905cd3Sdoug
18*c57a2cdeStb #include <stdint.h>
19c4905cd3Sdoug #include <string.h>
20c4905cd3Sdoug
21c4905cd3Sdoug #include "bytestring.h"
22c4905cd3Sdoug
23732f1cb2Sdoug /*
24732f1cb2Sdoug * kMaxDepth is a just a sanity limit. The code should be such that the length
25c4905cd3Sdoug * of the input being processes always decreases. None the less, a very large
26732f1cb2Sdoug * input could otherwise cause the stack to overflow.
27732f1cb2Sdoug */
288cb1c40eSdoug static const unsigned int kMaxDepth = 2048;
29c4905cd3Sdoug
300870ee34Sdoug /* Non-strict version that allows a relaxed DER with indefinite form. */
310870ee34Sdoug static int
cbs_nonstrict_get_any_asn1_element(CBS * cbs,CBS * out,unsigned int * out_tag,size_t * out_header_len)3250933c7bSdoug cbs_nonstrict_get_any_asn1_element(CBS *cbs, CBS *out, unsigned int *out_tag,
330870ee34Sdoug size_t *out_header_len)
340870ee34Sdoug {
350870ee34Sdoug return cbs_get_any_asn1_element_internal(cbs, out,
360870ee34Sdoug out_tag, out_header_len, 0);
370870ee34Sdoug }
380870ee34Sdoug
39732f1cb2Sdoug /*
4050933c7bSdoug * cbs_find_indefinite walks an ASN.1 structure in |orig_in| and sets
4150933c7bSdoug * |*indefinite_found| depending on whether an indefinite length element was
4250933c7bSdoug * found. The value of |orig_in| is not modified.
4350933c7bSdoug *
4450933c7bSdoug * Returns one on success (i.e. |*indefinite_found| was set) and zero on error.
45732f1cb2Sdoug */
46732f1cb2Sdoug static int
cbs_find_indefinite(const CBS * orig_in,char * indefinite_found,unsigned int depth)4750933c7bSdoug cbs_find_indefinite(const CBS *orig_in, char *indefinite_found,
4850933c7bSdoug unsigned int depth)
49732f1cb2Sdoug {
50c4905cd3Sdoug CBS in;
51c4905cd3Sdoug
52732f1cb2Sdoug if (depth > kMaxDepth)
53c4905cd3Sdoug return 0;
54c4905cd3Sdoug
55c4905cd3Sdoug CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in));
56c4905cd3Sdoug
57c4905cd3Sdoug while (CBS_len(&in) > 0) {
58c4905cd3Sdoug CBS contents;
598cb1c40eSdoug unsigned int tag;
60c4905cd3Sdoug size_t header_len;
61c4905cd3Sdoug
620870ee34Sdoug if (!cbs_nonstrict_get_any_asn1_element(&in, &contents, &tag,
63732f1cb2Sdoug &header_len))
64c4905cd3Sdoug return 0;
65732f1cb2Sdoug
660870ee34Sdoug /* Indefinite form not allowed by DER. */
67732f1cb2Sdoug if (CBS_len(&contents) == header_len && header_len > 0 &&
68c4905cd3Sdoug CBS_data(&contents)[header_len - 1] == 0x80) {
6950933c7bSdoug *indefinite_found = 1;
70c4905cd3Sdoug return 1;
71c4905cd3Sdoug }
72c4905cd3Sdoug if (tag & CBS_ASN1_CONSTRUCTED) {
73c4905cd3Sdoug if (!CBS_skip(&contents, header_len) ||
7450933c7bSdoug !cbs_find_indefinite(&contents, indefinite_found,
7550933c7bSdoug depth + 1))
76c4905cd3Sdoug return 0;
77c4905cd3Sdoug }
78c4905cd3Sdoug }
79c4905cd3Sdoug
8050933c7bSdoug *indefinite_found = 0;
81c4905cd3Sdoug return 1;
82c4905cd3Sdoug }
83c4905cd3Sdoug
84732f1cb2Sdoug /*
85732f1cb2Sdoug * is_primitive_type returns true if |tag| likely a primitive type. Normally
86c4905cd3Sdoug * one can just test the "constructed" bit in the tag but, in BER, even
87c4905cd3Sdoug * primitive tags can have the constructed bit if they have indefinite
88732f1cb2Sdoug * length.
89732f1cb2Sdoug */
90732f1cb2Sdoug static char
is_primitive_type(unsigned int tag)918cb1c40eSdoug is_primitive_type(unsigned int tag)
92732f1cb2Sdoug {
93c4905cd3Sdoug return (tag & 0xc0) == 0 &&
94c4905cd3Sdoug (tag & 0x1f) != (CBS_ASN1_SEQUENCE & 0x1f) &&
95c4905cd3Sdoug (tag & 0x1f) != (CBS_ASN1_SET & 0x1f);
96c4905cd3Sdoug }
97c4905cd3Sdoug
98732f1cb2Sdoug /*
99732f1cb2Sdoug * is_eoc returns true if |header_len| and |contents|, as returned by
1000870ee34Sdoug * |cbs_nonstrict_get_any_asn1_element|, indicate an "end of contents" (EOC)
1010870ee34Sdoug * value.
102732f1cb2Sdoug */
103732f1cb2Sdoug static char
is_eoc(size_t header_len,CBS * contents)104732f1cb2Sdoug is_eoc(size_t header_len, CBS *contents)
105732f1cb2Sdoug {
106bc99fb9aSjsing const unsigned char eoc[] = {0x0, 0x0};
107bc99fb9aSjsing
108bc99fb9aSjsing return header_len == 2 && CBS_mem_equal(contents, eoc, 2);
109c4905cd3Sdoug }
110c4905cd3Sdoug
111732f1cb2Sdoug /*
11250933c7bSdoug * cbs_convert_indefinite reads data with DER encoding (but relaxed to allow
11350933c7bSdoug * indefinite form) from |in| and writes definite form DER data to |out|. If
114c4905cd3Sdoug * |squash_header| is set then the top-level of elements from |in| will not
115c4905cd3Sdoug * have their headers written. This is used when concatenating the fragments of
116c4905cd3Sdoug * an indefinite length, primitive value. If |looking_for_eoc| is set then any
117c4905cd3Sdoug * EOC elements found will cause the function to return after consuming it.
118732f1cb2Sdoug * It returns one on success and zero on error.
119732f1cb2Sdoug */
120732f1cb2Sdoug static int
cbs_convert_indefinite(CBS * in,CBB * out,char squash_header,char looking_for_eoc,unsigned int depth)12150933c7bSdoug cbs_convert_indefinite(CBS *in, CBB *out, char squash_header,
1228cb1c40eSdoug char looking_for_eoc, unsigned int depth)
123732f1cb2Sdoug {
124732f1cb2Sdoug if (depth > kMaxDepth)
125c4905cd3Sdoug return 0;
126c4905cd3Sdoug
127c4905cd3Sdoug while (CBS_len(in) > 0) {
128c4905cd3Sdoug CBS contents;
1298cb1c40eSdoug unsigned int tag;
130c4905cd3Sdoug size_t header_len;
131c4905cd3Sdoug CBB *out_contents, out_contents_storage;
132c4905cd3Sdoug
1330870ee34Sdoug if (!cbs_nonstrict_get_any_asn1_element(in, &contents, &tag,
1340870ee34Sdoug &header_len))
135c4905cd3Sdoug return 0;
136732f1cb2Sdoug
137c4905cd3Sdoug out_contents = out;
138c4905cd3Sdoug
139c4905cd3Sdoug if (CBS_len(&contents) == header_len) {
140732f1cb2Sdoug if (is_eoc(header_len, &contents))
141c4905cd3Sdoug return looking_for_eoc;
142c4905cd3Sdoug
143732f1cb2Sdoug if (header_len > 0 &&
144732f1cb2Sdoug CBS_data(&contents)[header_len - 1] == 0x80) {
145732f1cb2Sdoug /*
146732f1cb2Sdoug * This is an indefinite length element. If
147732f1cb2Sdoug * it's a SEQUENCE or SET then we just need to
148732f1cb2Sdoug * write the out the contents as normal, but
149732f1cb2Sdoug * with a concrete length prefix.
150c4905cd3Sdoug *
151732f1cb2Sdoug * If it's a something else then the contents
15250933c7bSdoug * will be a series of DER elements of the same
153732f1cb2Sdoug * type which need to be concatenated.
154732f1cb2Sdoug */
155732f1cb2Sdoug const char context_specific = (tag & 0xc0)
156732f1cb2Sdoug == 0x80;
157732f1cb2Sdoug char squash_child_headers =
158732f1cb2Sdoug is_primitive_type(tag);
159c4905cd3Sdoug
160732f1cb2Sdoug /*
161732f1cb2Sdoug * This is a hack, but it sufficies to handle
162732f1cb2Sdoug * NSS's output. If we find an indefinite
163732f1cb2Sdoug * length, context-specific tag with a definite,
164732f1cb2Sdoug * primtive tag inside it, then we assume that
165732f1cb2Sdoug * the context-specific tag is implicit and the
166732f1cb2Sdoug * tags within are fragments of a primitive type
167732f1cb2Sdoug * that need to be concatenated.
168732f1cb2Sdoug */
169732f1cb2Sdoug if (context_specific &&
170732f1cb2Sdoug (tag & CBS_ASN1_CONSTRUCTED)) {
171c4905cd3Sdoug CBS in_copy, inner_contents;
1728cb1c40eSdoug unsigned int inner_tag;
173c4905cd3Sdoug size_t inner_header_len;
174c4905cd3Sdoug
175732f1cb2Sdoug CBS_init(&in_copy, CBS_data(in),
176732f1cb2Sdoug CBS_len(in));
1770870ee34Sdoug if (!cbs_nonstrict_get_any_asn1_element(
1780870ee34Sdoug &in_copy, &inner_contents,
1790870ee34Sdoug &inner_tag, &inner_header_len))
180c4905cd3Sdoug return 0;
181732f1cb2Sdoug
182732f1cb2Sdoug if (CBS_len(&inner_contents) >
183732f1cb2Sdoug inner_header_len &&
184732f1cb2Sdoug is_primitive_type(inner_tag))
185c4905cd3Sdoug squash_child_headers = 1;
186c4905cd3Sdoug }
187c4905cd3Sdoug
188c4905cd3Sdoug if (!squash_header) {
1898cb1c40eSdoug unsigned int out_tag = tag;
190732f1cb2Sdoug
191732f1cb2Sdoug if (squash_child_headers)
192732f1cb2Sdoug out_tag &=
193732f1cb2Sdoug ~CBS_ASN1_CONSTRUCTED;
194732f1cb2Sdoug
195732f1cb2Sdoug if (!CBB_add_asn1(out,
196732f1cb2Sdoug &out_contents_storage, out_tag))
197c4905cd3Sdoug return 0;
198732f1cb2Sdoug
199c4905cd3Sdoug out_contents = &out_contents_storage;
200c4905cd3Sdoug }
201c4905cd3Sdoug
20250933c7bSdoug if (!cbs_convert_indefinite(in, out_contents,
203c4905cd3Sdoug squash_child_headers,
204732f1cb2Sdoug 1 /* looking for eoc */, depth + 1))
205c4905cd3Sdoug return 0;
206732f1cb2Sdoug
207732f1cb2Sdoug if (out_contents != out && !CBB_flush(out))
208c4905cd3Sdoug return 0;
209732f1cb2Sdoug
210c4905cd3Sdoug continue;
211c4905cd3Sdoug }
212c4905cd3Sdoug }
213c4905cd3Sdoug
214c4905cd3Sdoug if (!squash_header) {
215732f1cb2Sdoug if (!CBB_add_asn1(out, &out_contents_storage, tag))
216c4905cd3Sdoug return 0;
217732f1cb2Sdoug
218c4905cd3Sdoug out_contents = &out_contents_storage;
219c4905cd3Sdoug }
220c4905cd3Sdoug
221732f1cb2Sdoug if (!CBS_skip(&contents, header_len))
222c4905cd3Sdoug return 0;
223c4905cd3Sdoug
224c4905cd3Sdoug if (tag & CBS_ASN1_CONSTRUCTED) {
22550933c7bSdoug if (!cbs_convert_indefinite(&contents, out_contents,
226732f1cb2Sdoug 0 /* don't squash header */,
227732f1cb2Sdoug 0 /* not looking for eoc */, depth + 1))
228c4905cd3Sdoug return 0;
229c4905cd3Sdoug } else {
230c4905cd3Sdoug if (!CBB_add_bytes(out_contents, CBS_data(&contents),
231732f1cb2Sdoug CBS_len(&contents)))
232c4905cd3Sdoug return 0;
233c4905cd3Sdoug }
234c4905cd3Sdoug
235732f1cb2Sdoug if (out_contents != out && !CBB_flush(out))
236c4905cd3Sdoug return 0;
237c4905cd3Sdoug }
238c4905cd3Sdoug
239c4905cd3Sdoug return looking_for_eoc == 0;
240c4905cd3Sdoug }
241c4905cd3Sdoug
242732f1cb2Sdoug int
CBS_asn1_indefinite_to_definite(CBS * in,uint8_t ** out,size_t * out_len)24350933c7bSdoug CBS_asn1_indefinite_to_definite(CBS *in, uint8_t **out, size_t *out_len)
244732f1cb2Sdoug {
245c4905cd3Sdoug CBB cbb;
246c4905cd3Sdoug
247732f1cb2Sdoug /*
248732f1cb2Sdoug * First, do a quick walk to find any indefinite-length elements. Most
249732f1cb2Sdoug * of the time we hope that there aren't any and thus we can quickly
250732f1cb2Sdoug * return.
251732f1cb2Sdoug */
252c4905cd3Sdoug char conversion_needed;
25350933c7bSdoug if (!cbs_find_indefinite(in, &conversion_needed, 0))
254c4905cd3Sdoug return 0;
255c4905cd3Sdoug
256c4905cd3Sdoug if (!conversion_needed) {
257c4905cd3Sdoug *out = NULL;
258c4905cd3Sdoug *out_len = 0;
259c4905cd3Sdoug return 1;
260c4905cd3Sdoug }
261c4905cd3Sdoug
2620d289efdSdoug if (!CBB_init(&cbb, CBS_len(in)))
2630d289efdSdoug return 0;
26450933c7bSdoug if (!cbs_convert_indefinite(in, &cbb, 0, 0, 0)) {
265c4905cd3Sdoug CBB_cleanup(&cbb);
266c4905cd3Sdoug return 0;
267c4905cd3Sdoug }
268c4905cd3Sdoug
269c4905cd3Sdoug return CBB_finish(&cbb, out, out_len);
270c4905cd3Sdoug }
271