xref: /openbsd-src/lib/libcrypto/asn1/asn1_lib.c (revision 0090c6809e04e08e1476b0c6a97c055d50f51ba5)
1 /* $OpenBSD: asn1_lib.c,v 1.52 2022/03/26 14:47:58 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 static 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 {
27 	uint8_t tag_class, tag_val;
28 	int tag_constructed;
29 	uint32_t tag_number;
30 
31 	/*
32 	 * Decode ASN.1 identifier octets - see ITU-T X.690 section 8.1.2.
33 	 */
34 
35 	*out_class = 0;
36 	*out_constructed = 0;
37 	*out_tag_number = 0;
38 
39 	if (!CBS_get_u8(cbs, &tag_val))
40 		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 	tag_class = (tag_val >> 6) & 0x3;
55 	tag_constructed = (tag_val >> 5) & 0x1;
56 	tag_number = tag_val & 0x1f;
57 
58 	/* Long form. */
59 	if (tag_number == 0x1f) {
60 		tag_number = 0;
61 		do {
62 			if (!CBS_get_u8(cbs, &tag_val))
63 				return 0;
64 			if (der_mode && tag_number == 0 && tag_val == 0x80)
65 				return 0;
66 			if (tag_number > (UINT32_MAX >> 7))
67 				return 0;
68 			tag_number = tag_number << 7 | (tag_val & 0x7f);
69 		} while ((tag_val & 0x80) != 0);
70 	}
71 
72 	*out_class = tag_class;
73 	*out_constructed = tag_constructed;
74 	*out_tag_number = tag_number;
75 
76 	return 1;
77 }
78 
79 static int
80 asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite,
81     uint32_t *out_length)
82 {
83 	uint8_t len_bytes;
84 	uint32_t length;
85 	uint8_t val;
86 
87 	/*
88 	 * Decode ASN.1 length octets - see ITU-T X.690 section 8.1.3.
89 	 */
90 
91 	*out_length = 0;
92 	*out_indefinite = 0;
93 
94 	if (!CBS_get_u8(cbs, &val))
95 		return 0;
96 
97 	/*
98 	 * Short form - length is encoded in the lower 7 bits of a single byte.
99 	 */
100 	if (val < 0x80) {
101 		*out_length = val;
102 		return 1;
103 	}
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 	if (val == 0x80) {
110 		*out_indefinite = 1;
111 		return 1;
112 	}
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 	if ((len_bytes = val & 0x7f) == 0x7f)
121 		return 0;
122 
123 	length = 0;
124 
125 	while (len_bytes-- > 0) {
126 		if (!CBS_get_u8(cbs, &val))
127 			return 0;
128 		if (der_mode && length == 0 && val == 0)
129 			return 0;
130 		if (length > (UINT32_MAX >> 8))
131 			return 0;
132 		length = (length << 8) | val;
133 	}
134 
135 	*out_length = length;
136 
137 	return 1;
138 }
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     uint32_t *out_length)
144 {
145 	int constructed, indefinite;
146 	uint32_t tag_number, length;
147 	uint8_t tag_class;
148 
149 	*out_tag_class = 0;
150 	*out_constructed = 0;
151 	*out_tag_number = 0;
152 	*out_indefinite = 0;
153 	*out_length = 0;
154 
155 	if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
156 	    &tag_number))
157 		return 0;
158 	if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
159 		return 0;
160 
161 	/* Indefinite length can only be used with constructed encoding. */
162 	if (indefinite && !constructed)
163 		return 0;
164 
165 	*out_tag_class = tag_class;
166 	*out_constructed = constructed;
167 	*out_tag_number = tag_number;
168 	*out_indefinite = indefinite;
169 	*out_length = length;
170 
171 	return 1;
172 }
173 
174 int
175 asn1_get_primitive(CBS *cbs, int der_mode, uint32_t *out_tag_number,
176     CBS *out_content)
177 {
178 	int constructed, indefinite;
179 	uint32_t tag_number, length;
180 	uint8_t tag_class;
181 
182 	*out_tag_number = 0;
183 
184 	CBS_init(out_content, NULL, 0);
185 
186 	if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
187 	    &tag_number))
188 		return 0;
189 	if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
190 		return 0;
191 
192 	/* A primitive is not constructed and has a definite length. */
193 	if (constructed || indefinite)
194 		return 0;
195 
196 	if (!CBS_get_bytes(cbs, out_content, length))
197 		return 0;
198 
199 	*out_tag_number = tag_number;
200 
201 	return 1;
202 }
203