xref: /dragonfly/crypto/libressl/crypto/asn1/asn1_lib.c (revision 961e30ea7dc61d1112b778ea4981eac68129fb86)
1 /* $OpenBSD: asn1_lib.c,v 1.54 2022/05/05 19:18:56 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 int
asn1_get_identifier_cbs(CBS * cbs,int der_mode,uint8_t * out_class,int * out_constructed,uint32_t * out_tag_number)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 int
asn1_get_length_cbs(CBS * cbs,int der_mode,int * out_indefinite,size_t * out_length)80 asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite,
81     size_t *out_length)
82 {
83           uint8_t len_bytes;
84           size_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 > (SIZE_MAX >> 8))
131                               return 0;
132                     length = (length << 8) | val;
133           }
134 
135           *out_length = length;
136 
137           return 1;
138 }
139 
140 int
asn1_get_object_cbs(CBS * cbs,int der_mode,uint8_t * out_tag_class,int * out_constructed,uint32_t * out_tag_number,int * out_indefinite,size_t * out_length)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     size_t *out_length)
144 {
145           int constructed, indefinite;
146           uint32_t tag_number;
147           uint8_t tag_class;
148           size_t length;
149 
150           *out_tag_class = 0;
151           *out_constructed = 0;
152           *out_tag_number = 0;
153           *out_indefinite = 0;
154           *out_length = 0;
155 
156           if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
157               &tag_number))
158                     return 0;
159           if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
160                     return 0;
161 
162           /* Indefinite length can only be used with constructed encoding. */
163           if (indefinite && !constructed)
164                     return 0;
165 
166           *out_tag_class = tag_class;
167           *out_constructed = constructed;
168           *out_tag_number = tag_number;
169           *out_indefinite = indefinite;
170           *out_length = length;
171 
172           return 1;
173 }
174 
175 int
asn1_get_primitive(CBS * cbs,int der_mode,uint32_t * out_tag_number,CBS * out_content)176 asn1_get_primitive(CBS *cbs, int der_mode, uint32_t *out_tag_number,
177     CBS *out_content)
178 {
179           int constructed, indefinite;
180           uint32_t tag_number;
181           uint8_t tag_class;
182           size_t length;
183 
184           *out_tag_number = 0;
185 
186           CBS_init(out_content, NULL, 0);
187 
188           if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
189               &tag_number))
190                     return 0;
191           if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
192                     return 0;
193 
194           /* A primitive is not constructed and has a definite length. */
195           if (constructed || indefinite)
196                     return 0;
197 
198           if (!CBS_get_bytes(cbs, out_content, length))
199                     return 0;
200 
201           *out_tag_number = tag_number;
202 
203           return 1;
204 }
205