xref: /openbsd-src/lib/libcrypto/asn1/asn1_lib.c (revision 9a10effcec5ce8f732978e29990eb94ca50711a7)
1 /* $OpenBSD: asn1_lib.c,v 1.51 2021/12/25 07:04:03 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 
20 #include "bytestring.h"
21 
22 static int
23 asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class,
24     int *out_constructed, uint32_t *out_tag_number)
25 {
26 	uint8_t tag_class, tag_val;
27 	int tag_constructed;
28 	uint32_t tag_number;
29 
30 	/*
31 	 * Decode ASN.1 identifier octets - see ITU-T X.690 section 8.1.2.
32 	 */
33 
34 	*out_class = 0;
35 	*out_constructed = 0;
36 	*out_tag_number = 0;
37 
38 	if (!CBS_get_u8(cbs, &tag_val))
39 		return 0;
40 
41 	/*
42 	 * ASN.1 tag class, encoding (primitive or constructed) and tag number
43 	 * are encoded in one or more identifier octets - the first octet
44 	 * contains the 2 bit tag class, the 1 bit encoding type and 5 bits
45 	 * of tag number.
46 	 *
47 	 * For tag numbers larger than 30 (0x1e) the 5 bit tag number in the
48 	 * first octet is set to all ones (0x1f) - the tag number is then
49 	 * encoded in subsequent octets - each of which have a one bit
50 	 * continuation flag and 7 bits of tag number in big-endian form.
51 	 * The encoding should not contain leading zeros but can for BER.
52 	 */
53 	tag_class = (tag_val >> 6) & 0x3;
54 	tag_constructed = (tag_val >> 5) & 0x1;
55 	tag_number = tag_val & 0x1f;
56 
57 	/* Long form. */
58 	if (tag_number == 0x1f) {
59 		tag_number = 0;
60 		do {
61 			if (!CBS_get_u8(cbs, &tag_val))
62 				return 0;
63 			if (der_mode && tag_number == 0 && tag_val == 0x80)
64 				return 0;
65 			if (tag_number > (UINT32_MAX >> 7))
66 				return 0;
67 			tag_number = tag_number << 7 | (tag_val & 0x7f);
68 		} while ((tag_val & 0x80) != 0);
69 	}
70 
71 	*out_class = tag_class;
72 	*out_constructed = tag_constructed;
73 	*out_tag_number = tag_number;
74 
75 	return 1;
76 }
77 
78 static int
79 asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite,
80     uint32_t *out_length)
81 {
82 	uint8_t len_bytes;
83 	uint32_t length;
84 	uint8_t val;
85 
86 	/*
87 	 * Decode ASN.1 length octets - see ITU-T X.690 section 8.1.3.
88 	 */
89 
90 	*out_length = 0;
91 	*out_indefinite = 0;
92 
93 	if (!CBS_get_u8(cbs, &val))
94 		return 0;
95 
96 	/*
97 	 * Short form - length is encoded in the lower 7 bits of a single byte.
98 	 */
99 	if (val < 0x80) {
100 		*out_length = val;
101 		return 1;
102 	}
103 
104 	/*
105 	 * Indefinite length - content continues until an End of Content (EOC)
106 	 * marker is reached. Must be used with constructed encoding.
107 	 */
108 	if (val == 0x80) {
109 		*out_indefinite = 1;
110 		return 1;
111 	}
112 
113 	/*
114 	 * Long form - the lower 7 bits of the first byte specifies the number
115 	 * of bytes used to encode the length, the following bytes specify the
116 	 * length in big-endian form. The encoding should not contain leading
117 	 * zeros but can for BER. A length value of 0x7f is invalid.
118 	 */
119 	if ((len_bytes = val & 0x7f) == 0x7f)
120 		return 0;
121 
122 	length = 0;
123 
124 	while (len_bytes-- > 0) {
125 		if (!CBS_get_u8(cbs, &val))
126 			return 0;
127 		if (der_mode && length == 0 && val == 0)
128 			return 0;
129 		if (length > (UINT32_MAX >> 8))
130 			return 0;
131 		length = (length << 8) | val;
132 	}
133 
134 	*out_length = length;
135 
136 	return 1;
137 }
138 
139 int
140 asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_tag_class,
141     int *out_constructed, uint32_t *out_tag_number, int *out_indefinite,
142     uint32_t *out_length)
143 {
144 	int constructed, indefinite;
145 	uint32_t tag_number, length;
146 	uint8_t tag_class;
147 
148 	*out_tag_class = 0;
149 	*out_constructed = 0;
150 	*out_tag_number = 0;
151 	*out_indefinite = 0;
152 	*out_length = 0;
153 
154 	if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
155 	    &tag_number))
156 		return 0;
157 	if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
158 		return 0;
159 
160 	/* Indefinite length can only be used with constructed encoding. */
161 	if (indefinite && !constructed)
162 		return 0;
163 
164 	*out_tag_class = tag_class;
165 	*out_constructed = constructed;
166 	*out_tag_number = tag_number;
167 	*out_indefinite = indefinite;
168 	*out_length = length;
169 
170 	return 1;
171 }
172