Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2014, Google Inc. |
| 2 | * |
| 3 | * Permission to use, copy, modify, and/or distribute this software for any |
| 4 | * purpose with or without fee is hereby granted, provided that the above |
| 5 | * copyright notice and this permission notice appear in all copies. |
| 6 | * |
| 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 10 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
| 14 | |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 15 | #include <openssl/asn1.h> |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 16 | #include <openssl/bytestring.h> |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 17 | #include <openssl/mem.h> |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 18 | |
| 19 | #include <assert.h> |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 20 | #include <ctype.h> |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 21 | #include <inttypes.h> |
David Benjamin | ed43958 | 2014-07-14 19:13:02 -0400 | [diff] [blame] | 22 | #include <string.h> |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 23 | |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 24 | #include "../asn1/internal.h" |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 25 | #include "../internal.h" |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 26 | #include "internal.h" |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 27 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 28 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 29 | static int cbs_get(CBS *cbs, const uint8_t **p, size_t n) { |
| 30 | if (cbs->len < n) { |
| 31 | return 0; |
| 32 | } |
| 33 | |
| 34 | *p = cbs->data; |
| 35 | cbs->data += n; |
| 36 | cbs->len -= n; |
| 37 | return 1; |
| 38 | } |
| 39 | |
| 40 | int CBS_skip(CBS *cbs, size_t len) { |
| 41 | const uint8_t *dummy; |
| 42 | return cbs_get(cbs, &dummy, len); |
| 43 | } |
| 44 | |
David Benjamin | 0397309 | 2014-06-24 23:27:17 -0400 | [diff] [blame] | 45 | int CBS_stow(const CBS *cbs, uint8_t **out_ptr, size_t *out_len) { |
David Benjamin | 22ccc2d | 2015-04-22 13:50:28 -0400 | [diff] [blame] | 46 | OPENSSL_free(*out_ptr); |
| 47 | *out_ptr = NULL; |
David Benjamin | 0397309 | 2014-06-24 23:27:17 -0400 | [diff] [blame] | 48 | *out_len = 0; |
| 49 | |
| 50 | if (cbs->len == 0) { |
| 51 | return 1; |
| 52 | } |
David Benjamin | 3ba9586 | 2019-10-21 16:14:33 -0400 | [diff] [blame] | 53 | *out_ptr = OPENSSL_memdup(cbs->data, cbs->len); |
David Benjamin | 0397309 | 2014-06-24 23:27:17 -0400 | [diff] [blame] | 54 | if (*out_ptr == NULL) { |
| 55 | return 0; |
| 56 | } |
| 57 | *out_len = cbs->len; |
| 58 | return 1; |
| 59 | } |
| 60 | |
David Benjamin | ed43958 | 2014-07-14 19:13:02 -0400 | [diff] [blame] | 61 | int CBS_strdup(const CBS *cbs, char **out_ptr) { |
| 62 | if (*out_ptr != NULL) { |
| 63 | OPENSSL_free(*out_ptr); |
| 64 | } |
David Benjamin | 3ba9586 | 2019-10-21 16:14:33 -0400 | [diff] [blame] | 65 | *out_ptr = OPENSSL_strndup((const char*)cbs->data, cbs->len); |
David Benjamin | ed43958 | 2014-07-14 19:13:02 -0400 | [diff] [blame] | 66 | return (*out_ptr != NULL); |
| 67 | } |
| 68 | |
| 69 | int CBS_contains_zero_byte(const CBS *cbs) { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 70 | return OPENSSL_memchr(cbs->data, 0, cbs->len) != NULL; |
David Benjamin | 0397309 | 2014-06-24 23:27:17 -0400 | [diff] [blame] | 71 | } |
| 72 | |
David Benjamin | 22f9bcc | 2014-07-13 12:29:21 -0400 | [diff] [blame] | 73 | int CBS_mem_equal(const CBS *cbs, const uint8_t *data, size_t len) { |
David Benjamin | c9a202f | 2015-02-11 01:16:26 -0500 | [diff] [blame] | 74 | if (len != cbs->len) { |
David Benjamin | 22f9bcc | 2014-07-13 12:29:21 -0400 | [diff] [blame] | 75 | return 0; |
David Benjamin | c9a202f | 2015-02-11 01:16:26 -0500 | [diff] [blame] | 76 | } |
David Benjamin | 22f9bcc | 2014-07-13 12:29:21 -0400 | [diff] [blame] | 77 | return CRYPTO_memcmp(cbs->data, data, len) == 0; |
| 78 | } |
| 79 | |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 80 | static int cbs_get_u(CBS *cbs, uint64_t *out, size_t len) { |
| 81 | uint64_t result = 0; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 82 | const uint8_t *data; |
| 83 | |
| 84 | if (!cbs_get(cbs, &data, len)) { |
| 85 | return 0; |
| 86 | } |
David Benjamin | 5409123 | 2016-09-05 12:47:25 -0400 | [diff] [blame] | 87 | for (size_t i = 0; i < len; i++) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 88 | result <<= 8; |
| 89 | result |= data[i]; |
| 90 | } |
| 91 | *out = result; |
| 92 | return 1; |
| 93 | } |
| 94 | |
| 95 | int CBS_get_u8(CBS *cbs, uint8_t *out) { |
| 96 | const uint8_t *v; |
| 97 | if (!cbs_get(cbs, &v, 1)) { |
| 98 | return 0; |
| 99 | } |
| 100 | *out = *v; |
| 101 | return 1; |
| 102 | } |
| 103 | |
| 104 | int CBS_get_u16(CBS *cbs, uint16_t *out) { |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 105 | uint64_t v; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 106 | if (!cbs_get_u(cbs, &v, 2)) { |
| 107 | return 0; |
| 108 | } |
| 109 | *out = v; |
| 110 | return 1; |
| 111 | } |
| 112 | |
Adam Langley | a965a25 | 2020-01-15 16:08:03 -0800 | [diff] [blame] | 113 | int CBS_get_u16le(CBS *cbs, uint16_t *out) { |
| 114 | if (!CBS_get_u16(cbs, out)) { |
| 115 | return 0; |
| 116 | } |
| 117 | *out = CRYPTO_bswap2(*out); |
| 118 | return 1; |
| 119 | } |
| 120 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 121 | int CBS_get_u24(CBS *cbs, uint32_t *out) { |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 122 | uint64_t v; |
| 123 | if (!cbs_get_u(cbs, &v, 3)) { |
| 124 | return 0; |
| 125 | } |
David Benjamin | 7ac94aa | 2022-10-24 15:44:37 -0400 | [diff] [blame] | 126 | *out = (uint32_t)v; |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 127 | return 1; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | int CBS_get_u32(CBS *cbs, uint32_t *out) { |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 131 | uint64_t v; |
| 132 | if (!cbs_get_u(cbs, &v, 4)) { |
| 133 | return 0; |
| 134 | } |
David Benjamin | 7ac94aa | 2022-10-24 15:44:37 -0400 | [diff] [blame] | 135 | *out = (uint32_t)v; |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 136 | return 1; |
| 137 | } |
| 138 | |
Adam Langley | a965a25 | 2020-01-15 16:08:03 -0800 | [diff] [blame] | 139 | int CBS_get_u32le(CBS *cbs, uint32_t *out) { |
| 140 | if (!CBS_get_u32(cbs, out)) { |
| 141 | return 0; |
| 142 | } |
| 143 | *out = CRYPTO_bswap4(*out); |
| 144 | return 1; |
| 145 | } |
| 146 | |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 147 | int CBS_get_u64(CBS *cbs, uint64_t *out) { |
| 148 | return cbs_get_u(cbs, out, 8); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 149 | } |
| 150 | |
Adam Langley | a965a25 | 2020-01-15 16:08:03 -0800 | [diff] [blame] | 151 | int CBS_get_u64le(CBS *cbs, uint64_t *out) { |
| 152 | if (!cbs_get_u(cbs, out, 8)) { |
| 153 | return 0; |
| 154 | } |
| 155 | *out = CRYPTO_bswap8(*out); |
| 156 | return 1; |
| 157 | } |
| 158 | |
David Benjamin | a7810c1 | 2016-06-06 18:54:51 -0400 | [diff] [blame] | 159 | int CBS_get_last_u8(CBS *cbs, uint8_t *out) { |
| 160 | if (cbs->len == 0) { |
| 161 | return 0; |
| 162 | } |
| 163 | *out = cbs->data[cbs->len - 1]; |
| 164 | cbs->len--; |
| 165 | return 1; |
| 166 | } |
| 167 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 168 | int CBS_get_bytes(CBS *cbs, CBS *out, size_t len) { |
| 169 | const uint8_t *v; |
| 170 | if (!cbs_get(cbs, &v, len)) { |
| 171 | return 0; |
| 172 | } |
| 173 | CBS_init(out, v, len); |
| 174 | return 1; |
| 175 | } |
| 176 | |
David Benjamin | b8d28cf | 2015-07-28 21:34:45 -0400 | [diff] [blame] | 177 | int CBS_copy_bytes(CBS *cbs, uint8_t *out, size_t len) { |
| 178 | const uint8_t *v; |
| 179 | if (!cbs_get(cbs, &v, len)) { |
| 180 | return 0; |
| 181 | } |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 182 | OPENSSL_memcpy(out, v, len); |
David Benjamin | b8d28cf | 2015-07-28 21:34:45 -0400 | [diff] [blame] | 183 | return 1; |
| 184 | } |
| 185 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 186 | static int cbs_get_length_prefixed(CBS *cbs, CBS *out, size_t len_len) { |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 187 | uint64_t len; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 188 | if (!cbs_get_u(cbs, &len, len_len)) { |
| 189 | return 0; |
| 190 | } |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 191 | // If |len_len| <= 3 then we know that |len| will fit into a |size_t|, even on |
| 192 | // 32-bit systems. |
| 193 | assert(len_len <= 3); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 194 | return CBS_get_bytes(cbs, out, len); |
| 195 | } |
| 196 | |
| 197 | int CBS_get_u8_length_prefixed(CBS *cbs, CBS *out) { |
| 198 | return cbs_get_length_prefixed(cbs, out, 1); |
| 199 | } |
| 200 | |
| 201 | int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out) { |
| 202 | return cbs_get_length_prefixed(cbs, out, 2); |
| 203 | } |
| 204 | |
| 205 | int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) { |
| 206 | return cbs_get_length_prefixed(cbs, out, 3); |
| 207 | } |
| 208 | |
David Benjamin | b27438e | 2021-08-24 14:24:38 -0400 | [diff] [blame] | 209 | int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c) { |
| 210 | const uint8_t *split = OPENSSL_memchr(CBS_data(cbs), c, CBS_len(cbs)); |
| 211 | if (split == NULL) { |
| 212 | return 0; |
| 213 | } |
| 214 | return CBS_get_bytes(cbs, out, split - CBS_data(cbs)); |
| 215 | } |
| 216 | |
David Benjamin | 50a5caf | 2022-12-28 18:33:18 -0500 | [diff] [blame] | 217 | int CBS_get_u64_decimal(CBS *cbs, uint64_t *out) { |
| 218 | uint64_t v = 0; |
| 219 | int seen_digit = 0; |
| 220 | while (CBS_len(cbs) != 0) { |
| 221 | uint8_t c = CBS_data(cbs)[0]; |
Bob Beck | f86a63c | 2023-01-30 12:17:39 -0700 | [diff] [blame] | 222 | if (!OPENSSL_isdigit(c)) { |
David Benjamin | 50a5caf | 2022-12-28 18:33:18 -0500 | [diff] [blame] | 223 | break; |
| 224 | } |
| 225 | CBS_skip(cbs, 1); |
| 226 | if (// Forbid stray leading zeros. |
| 227 | (v == 0 && seen_digit) || |
| 228 | // Check for overflow. |
| 229 | v > UINT64_MAX / 10 || // |
| 230 | v * 10 > UINT64_MAX - (c - '0')) { |
| 231 | return 0; |
| 232 | } |
| 233 | v = v * 10 + (c - '0'); |
| 234 | seen_digit = 1; |
| 235 | } |
| 236 | |
| 237 | *out = v; |
| 238 | return seen_digit; |
| 239 | } |
| 240 | |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 241 | // parse_base128_integer reads a big-endian base-128 integer from |cbs| and sets |
| 242 | // |*out| to the result. This is the encoding used in DER for both high tag |
| 243 | // number form and OID components. |
| 244 | static int parse_base128_integer(CBS *cbs, uint64_t *out) { |
| 245 | uint64_t v = 0; |
| 246 | uint8_t b; |
| 247 | do { |
| 248 | if (!CBS_get_u8(cbs, &b)) { |
| 249 | return 0; |
| 250 | } |
| 251 | if ((v >> (64 - 7)) != 0) { |
| 252 | // The value is too large. |
| 253 | return 0; |
| 254 | } |
| 255 | if (v == 0 && b == 0x80) { |
| 256 | // The value must be minimally encoded. |
| 257 | return 0; |
| 258 | } |
| 259 | v = (v << 7) | (b & 0x7f); |
| 260 | |
| 261 | // Values end at an octet with the high bit cleared. |
| 262 | } while (b & 0x80); |
| 263 | |
| 264 | *out = v; |
| 265 | return 1; |
| 266 | } |
| 267 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 268 | static int parse_asn1_tag(CBS *cbs, CBS_ASN1_TAG *out) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 269 | uint8_t tag_byte; |
| 270 | if (!CBS_get_u8(cbs, &tag_byte)) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 271 | return 0; |
| 272 | } |
| 273 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 274 | // ITU-T X.690 section 8.1.2.3 specifies the format for identifiers with a tag |
| 275 | // number no greater than 30. |
| 276 | // |
| 277 | // If the number portion is 31 (0x1f, the largest value that fits in the |
| 278 | // allotted bits), then the tag is more than one byte long and the |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 279 | // continuation bytes contain the tag number. |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 280 | CBS_ASN1_TAG tag = ((CBS_ASN1_TAG)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT; |
| 281 | CBS_ASN1_TAG tag_number = tag_byte & 0x1f; |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 282 | if (tag_number == 0x1f) { |
| 283 | uint64_t v; |
| 284 | if (!parse_base128_integer(cbs, &v) || |
| 285 | // Check the tag number is within our supported bounds. |
| 286 | v > CBS_ASN1_TAG_NUMBER_MASK || |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 287 | // Small tag numbers should have used low tag number form, even in BER. |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 288 | v < 0x1f) { |
| 289 | return 0; |
| 290 | } |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 291 | tag_number = (CBS_ASN1_TAG)v; |
David Benjamin | 66801fe | 2017-11-21 16:25:12 -0500 | [diff] [blame] | 292 | } |
David Benjamin | 2fc4f36 | 2017-11-30 16:41:24 -0500 | [diff] [blame] | 293 | |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 294 | tag |= tag_number; |
| 295 | |
David Benjamin | edbdc24 | 2021-11-02 11:37:22 -0400 | [diff] [blame] | 296 | // Tag [UNIVERSAL 0] is reserved for use by the encoding. Reject it here to |
| 297 | // avoid some ambiguity around ANY values and BER indefinite-length EOCs. See |
| 298 | // https://crbug.com/boringssl/455. |
| 299 | if ((tag & ~CBS_ASN1_CONSTRUCTED) == 0) { |
| 300 | return 0; |
| 301 | } |
| 302 | |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 303 | *out = tag; |
| 304 | return 1; |
| 305 | } |
| 306 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 307 | static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, CBS_ASN1_TAG *out_tag, |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 308 | size_t *out_header_len, int *out_ber_found, |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 309 | int *out_indefinite, int ber_ok) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 310 | CBS header = *cbs; |
| 311 | CBS throwaway; |
| 312 | |
| 313 | if (out == NULL) { |
| 314 | out = &throwaway; |
| 315 | } |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 316 | if (ber_ok) { |
| 317 | *out_ber_found = 0; |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 318 | *out_indefinite = 0; |
| 319 | } else { |
| 320 | assert(out_ber_found == NULL); |
| 321 | assert(out_indefinite == NULL); |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 322 | } |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 323 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 324 | CBS_ASN1_TAG tag; |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 325 | if (!parse_asn1_tag(&header, &tag)) { |
| 326 | return 0; |
| 327 | } |
David Benjamin | 9b04d65 | 2014-08-27 20:19:50 -0400 | [diff] [blame] | 328 | if (out_tag != NULL) { |
| 329 | *out_tag = tag; |
| 330 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 331 | |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 332 | uint8_t length_byte; |
| 333 | if (!CBS_get_u8(&header, &length_byte)) { |
| 334 | return 0; |
| 335 | } |
| 336 | |
| 337 | size_t header_len = CBS_len(cbs) - CBS_len(&header); |
| 338 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 339 | size_t len; |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 340 | // The format for the length encoding is specified in ITU-T X.690 section |
| 341 | // 8.1.3. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 342 | if ((length_byte & 0x80) == 0) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 343 | // Short form length. |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 344 | len = ((size_t) length_byte) + header_len; |
David Benjamin | 9b04d65 | 2014-08-27 20:19:50 -0400 | [diff] [blame] | 345 | if (out_header_len != NULL) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 346 | *out_header_len = header_len; |
David Benjamin | 9b04d65 | 2014-08-27 20:19:50 -0400 | [diff] [blame] | 347 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 348 | } else { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 349 | // The high bit indicate that this is the long form, while the next 7 bits |
| 350 | // encode the number of subsequent octets used to encode the length (ITU-T |
| 351 | // X.690 clause 8.1.3.5.b). |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 352 | const size_t num_bytes = length_byte & 0x7f; |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 353 | uint64_t len64; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 354 | |
Adam Langley | cc1e3df | 2015-06-02 15:49:48 -0700 | [diff] [blame] | 355 | if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 356 | // indefinite length |
Adam Langley | 0976096 | 2015-06-02 15:48:37 -0700 | [diff] [blame] | 357 | if (out_header_len != NULL) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 358 | *out_header_len = header_len; |
Adam Langley | 0976096 | 2015-06-02 15:48:37 -0700 | [diff] [blame] | 359 | } |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 360 | *out_ber_found = 1; |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 361 | *out_indefinite = 1; |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 362 | return CBS_get_bytes(cbs, out, header_len); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 363 | } |
| 364 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 365 | // ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be |
| 366 | // used as the first byte of the length. If this parser encounters that |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 367 | // value, num_bytes will be parsed as 127, which will fail this check. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 368 | if (num_bytes == 0 || num_bytes > 4) { |
| 369 | return 0; |
| 370 | } |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 371 | if (!cbs_get_u(&header, &len64, num_bytes)) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 372 | return 0; |
| 373 | } |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 374 | // ITU-T X.690 section 10.1 (DER length forms) requires encoding the |
| 375 | // length with the minimum number of octets. BER could, technically, have |
| 376 | // 125 superfluous zero bytes. We do not attempt to handle that and still |
| 377 | // require that the length fit in a |uint32_t| for BER. |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 378 | if (len64 < 128) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 379 | // Length should have used short-form encoding. |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 380 | if (ber_ok) { |
| 381 | *out_ber_found = 1; |
| 382 | } else { |
| 383 | return 0; |
| 384 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 385 | } |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 386 | if ((len64 >> ((num_bytes - 1) * 8)) == 0) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 387 | // Length should have been at least one byte shorter. |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 388 | if (ber_ok) { |
| 389 | *out_ber_found = 1; |
| 390 | } else { |
| 391 | return 0; |
| 392 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 393 | } |
Kaustubha Govind | c18353d | 2019-02-21 12:40:06 -0500 | [diff] [blame] | 394 | len = len64; |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 395 | if (len + header_len + num_bytes < len) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 396 | // Overflow. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 397 | return 0; |
| 398 | } |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 399 | len += header_len + num_bytes; |
David Benjamin | 9b04d65 | 2014-08-27 20:19:50 -0400 | [diff] [blame] | 400 | if (out_header_len != NULL) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 401 | *out_header_len = header_len + num_bytes; |
David Benjamin | 9b04d65 | 2014-08-27 20:19:50 -0400 | [diff] [blame] | 402 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 403 | } |
| 404 | |
| 405 | return CBS_get_bytes(cbs, out, len); |
| 406 | } |
| 407 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 408 | int CBS_get_any_asn1(CBS *cbs, CBS *out, CBS_ASN1_TAG *out_tag) { |
David Benjamin | 455919d | 2016-09-30 15:05:32 -0400 | [diff] [blame] | 409 | size_t header_len; |
| 410 | if (!CBS_get_any_asn1_element(cbs, out, out_tag, &header_len)) { |
| 411 | return 0; |
| 412 | } |
| 413 | |
| 414 | if (!CBS_skip(out, header_len)) { |
| 415 | assert(0); |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | return 1; |
| 420 | } |
| 421 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 422 | int CBS_get_any_asn1_element(CBS *cbs, CBS *out, CBS_ASN1_TAG *out_tag, |
Adam Langley | cc1e3df | 2015-06-02 15:49:48 -0700 | [diff] [blame] | 423 | size_t *out_header_len) { |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 424 | return cbs_get_any_asn1_element(cbs, out, out_tag, out_header_len, NULL, NULL, |
| 425 | /*ber_ok=*/0); |
Adam Langley | cc1e3df | 2015-06-02 15:49:48 -0700 | [diff] [blame] | 426 | } |
| 427 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 428 | int CBS_get_any_ber_asn1_element(CBS *cbs, CBS *out, CBS_ASN1_TAG *out_tag, |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 429 | size_t *out_header_len, int *out_ber_found, |
| 430 | int *out_indefinite) { |
Adam Langley | e56dfcf | 2020-12-16 15:11:27 -0800 | [diff] [blame] | 431 | int ber_found_temp; |
| 432 | return cbs_get_any_asn1_element( |
| 433 | cbs, out, out_tag, out_header_len, |
David Benjamin | 7fac386 | 2021-10-30 12:37:44 -0400 | [diff] [blame] | 434 | out_ber_found ? out_ber_found : &ber_found_temp, out_indefinite, |
| 435 | /*ber_ok=*/1); |
Adam Langley | cc1e3df | 2015-06-02 15:49:48 -0700 | [diff] [blame] | 436 | } |
| 437 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 438 | static int cbs_get_asn1(CBS *cbs, CBS *out, CBS_ASN1_TAG tag_value, |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 439 | int skip_header) { |
| 440 | size_t header_len; |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 441 | CBS_ASN1_TAG tag; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 442 | CBS throwaway; |
| 443 | |
| 444 | if (out == NULL) { |
| 445 | out = &throwaway; |
| 446 | } |
| 447 | |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 448 | if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || |
Adam Langley | cc1e3df | 2015-06-02 15:49:48 -0700 | [diff] [blame] | 449 | tag != tag_value) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 450 | return 0; |
| 451 | } |
| 452 | |
| 453 | if (skip_header && !CBS_skip(out, header_len)) { |
| 454 | assert(0); |
| 455 | return 0; |
| 456 | } |
| 457 | |
| 458 | return 1; |
| 459 | } |
| 460 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 461 | int CBS_get_asn1(CBS *cbs, CBS *out, CBS_ASN1_TAG tag_value) { |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 462 | return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 463 | } |
| 464 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 465 | int CBS_get_asn1_element(CBS *cbs, CBS *out, CBS_ASN1_TAG tag_value) { |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 466 | return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */); |
| 467 | } |
| 468 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 469 | int CBS_peek_asn1_tag(const CBS *cbs, CBS_ASN1_TAG tag_value) { |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 470 | CBS copy = *cbs; |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 471 | CBS_ASN1_TAG actual_tag; |
David Benjamin | d9f4997 | 2017-12-21 23:11:23 -0500 | [diff] [blame] | 472 | return parse_asn1_tag(©, &actual_tag) && tag_value == actual_tag; |
David Benjamin | b698617 | 2014-10-09 19:39:15 -0400 | [diff] [blame] | 473 | } |
| 474 | |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 475 | int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) { |
| 476 | CBS bytes; |
David Benjamin | 354e1e9 | 2020-09-04 19:01:50 -0400 | [diff] [blame] | 477 | if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER) || |
| 478 | !CBS_is_unsigned_asn1_integer(&bytes)) { |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 479 | return 0; |
| 480 | } |
| 481 | |
| 482 | *out = 0; |
David Benjamin | 76dd180 | 2017-04-17 10:04:39 -0400 | [diff] [blame] | 483 | const uint8_t *data = CBS_data(&bytes); |
| 484 | size_t len = CBS_len(&bytes); |
David Benjamin | 76dd180 | 2017-04-17 10:04:39 -0400 | [diff] [blame] | 485 | for (size_t i = 0; i < len; i++) { |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 486 | if ((*out >> 56) != 0) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 487 | // Too large to represent as a uint64_t. |
Adam Langley | 9c01e00 | 2014-08-21 10:54:06 -0700 | [diff] [blame] | 488 | return 0; |
| 489 | } |
| 490 | *out <<= 8; |
| 491 | *out |= data[i]; |
| 492 | } |
| 493 | |
| 494 | return 1; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 495 | } |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 496 | |
Matthew Braithwaite | 82dbb53 | 2019-12-10 12:18:42 -0800 | [diff] [blame] | 497 | int CBS_get_asn1_int64(CBS *cbs, int64_t *out) { |
David Benjamin | 354e1e9 | 2020-09-04 19:01:50 -0400 | [diff] [blame] | 498 | int is_negative; |
Matthew Braithwaite | 82dbb53 | 2019-12-10 12:18:42 -0800 | [diff] [blame] | 499 | CBS bytes; |
David Benjamin | 354e1e9 | 2020-09-04 19:01:50 -0400 | [diff] [blame] | 500 | if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER) || |
| 501 | !CBS_is_valid_asn1_integer(&bytes, &is_negative)) { |
Matthew Braithwaite | 82dbb53 | 2019-12-10 12:18:42 -0800 | [diff] [blame] | 502 | return 0; |
| 503 | } |
| 504 | const uint8_t *data = CBS_data(&bytes); |
| 505 | const size_t len = CBS_len(&bytes); |
David Benjamin | 354e1e9 | 2020-09-04 19:01:50 -0400 | [diff] [blame] | 506 | if (len > sizeof(int64_t)) { |
Matthew Braithwaite | 82dbb53 | 2019-12-10 12:18:42 -0800 | [diff] [blame] | 507 | return 0; |
| 508 | } |
David Benjamin | 1e469e4 | 2022-05-13 17:06:56 -0400 | [diff] [blame] | 509 | uint8_t sign_extend[sizeof(int64_t)]; |
David Benjamin | 45b2464 | 2024-06-21 17:00:31 -0400 | [diff] [blame] | 510 | OPENSSL_memset(sign_extend, is_negative ? 0xff : 0, sizeof(sign_extend)); |
| 511 | OPENSSL_memcpy(sign_extend + sizeof(int64_t) - len, data, len); |
| 512 | *out = CRYPTO_load_u64_be(sign_extend); |
Matthew Braithwaite | 82dbb53 | 2019-12-10 12:18:42 -0800 | [diff] [blame] | 513 | return 1; |
| 514 | } |
| 515 | |
Adam Langley | c61b577 | 2018-01-25 15:37:46 -0800 | [diff] [blame] | 516 | int CBS_get_asn1_bool(CBS *cbs, int *out) { |
| 517 | CBS bytes; |
| 518 | if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_BOOLEAN) || |
| 519 | CBS_len(&bytes) != 1) { |
| 520 | return 0; |
| 521 | } |
| 522 | |
| 523 | const uint8_t value = *CBS_data(&bytes); |
| 524 | if (value != 0 && value != 0xff) { |
| 525 | return 0; |
| 526 | } |
| 527 | |
| 528 | *out = !!value; |
| 529 | return 1; |
| 530 | } |
| 531 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 532 | int CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present, CBS_ASN1_TAG tag) { |
Paul Lietar | 23b185a | 2015-09-03 14:40:21 +0100 | [diff] [blame] | 533 | int present = 0; |
| 534 | |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 535 | if (CBS_peek_asn1_tag(cbs, tag)) { |
| 536 | if (!CBS_get_asn1(cbs, out, tag)) { |
| 537 | return 0; |
| 538 | } |
Paul Lietar | 23b185a | 2015-09-03 14:40:21 +0100 | [diff] [blame] | 539 | present = 1; |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 540 | } |
Paul Lietar | 23b185a | 2015-09-03 14:40:21 +0100 | [diff] [blame] | 541 | |
| 542 | if (out_present != NULL) { |
| 543 | *out_present = present; |
| 544 | } |
| 545 | |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 546 | return 1; |
| 547 | } |
| 548 | |
| 549 | int CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out, int *out_present, |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 550 | CBS_ASN1_TAG tag) { |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 551 | CBS child; |
| 552 | int present; |
| 553 | if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { |
| 554 | return 0; |
| 555 | } |
| 556 | if (present) { |
Adam Langley | d096c06 | 2018-03-15 13:54:33 -0700 | [diff] [blame] | 557 | assert(out); |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 558 | if (!CBS_get_asn1(&child, out, CBS_ASN1_OCTETSTRING) || |
| 559 | CBS_len(&child) != 0) { |
| 560 | return 0; |
| 561 | } |
| 562 | } else { |
| 563 | CBS_init(out, NULL, 0); |
| 564 | } |
| 565 | if (out_present) { |
| 566 | *out_present = present; |
| 567 | } |
| 568 | return 1; |
| 569 | } |
| 570 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 571 | int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, CBS_ASN1_TAG tag, |
David Benjamin | 83fd6b6 | 2014-10-19 04:33:38 -0400 | [diff] [blame] | 572 | uint64_t default_value) { |
| 573 | CBS child; |
| 574 | int present; |
| 575 | if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { |
| 576 | return 0; |
| 577 | } |
| 578 | if (present) { |
| 579 | if (!CBS_get_asn1_uint64(&child, out) || |
| 580 | CBS_len(&child) != 0) { |
| 581 | return 0; |
| 582 | } |
| 583 | } else { |
| 584 | *out = default_value; |
| 585 | } |
| 586 | return 1; |
| 587 | } |
Adam Langley | 7571292 | 2014-10-10 16:23:43 -0700 | [diff] [blame] | 588 | |
David Benjamin | a1dffbf | 2022-10-25 16:29:43 -0400 | [diff] [blame] | 589 | int CBS_get_optional_asn1_bool(CBS *cbs, int *out, CBS_ASN1_TAG tag, |
Adam Langley | 7571292 | 2014-10-10 16:23:43 -0700 | [diff] [blame] | 590 | int default_value) { |
| 591 | CBS child, child2; |
| 592 | int present; |
| 593 | if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { |
| 594 | return 0; |
| 595 | } |
| 596 | if (present) { |
| 597 | uint8_t boolean; |
| 598 | |
| 599 | if (!CBS_get_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || |
| 600 | CBS_len(&child2) != 1 || |
| 601 | CBS_len(&child) != 0) { |
| 602 | return 0; |
| 603 | } |
| 604 | |
| 605 | boolean = CBS_data(&child2)[0]; |
| 606 | if (boolean == 0) { |
| 607 | *out = 0; |
| 608 | } else if (boolean == 0xff) { |
| 609 | *out = 1; |
| 610 | } else { |
| 611 | return 0; |
| 612 | } |
| 613 | } else { |
| 614 | *out = default_value; |
| 615 | } |
| 616 | return 1; |
| 617 | } |
David Benjamin | fcd714d | 2016-12-13 20:48:19 -0500 | [diff] [blame] | 618 | |
| 619 | int CBS_is_valid_asn1_bitstring(const CBS *cbs) { |
| 620 | CBS in = *cbs; |
| 621 | uint8_t num_unused_bits; |
| 622 | if (!CBS_get_u8(&in, &num_unused_bits) || |
| 623 | num_unused_bits > 7) { |
| 624 | return 0; |
| 625 | } |
| 626 | |
| 627 | if (num_unused_bits == 0) { |
| 628 | return 1; |
| 629 | } |
| 630 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 631 | // All num_unused_bits bits must exist and be zeros. |
David Benjamin | fcd714d | 2016-12-13 20:48:19 -0500 | [diff] [blame] | 632 | uint8_t last; |
| 633 | if (!CBS_get_last_u8(&in, &last) || |
| 634 | (last & ((1 << num_unused_bits) - 1)) != 0) { |
| 635 | return 0; |
| 636 | } |
| 637 | |
| 638 | return 1; |
| 639 | } |
| 640 | |
| 641 | int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit) { |
| 642 | if (!CBS_is_valid_asn1_bitstring(cbs)) { |
| 643 | return 0; |
| 644 | } |
| 645 | |
| 646 | const unsigned byte_num = (bit >> 3) + 1; |
| 647 | const unsigned bit_num = 7 - (bit & 7); |
| 648 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 649 | // Unused bits are zero, and this function does not distinguish between |
| 650 | // missing and unset bits. Thus it is sufficient to do a byte-level length |
| 651 | // check. |
David Benjamin | fcd714d | 2016-12-13 20:48:19 -0500 | [diff] [blame] | 652 | return byte_num < CBS_len(cbs) && |
| 653 | (CBS_data(cbs)[byte_num] & (1 << bit_num)) != 0; |
| 654 | } |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 655 | |
David Benjamin | 354e1e9 | 2020-09-04 19:01:50 -0400 | [diff] [blame] | 656 | int CBS_is_valid_asn1_integer(const CBS *cbs, int *out_is_negative) { |
| 657 | CBS copy = *cbs; |
| 658 | uint8_t first_byte, second_byte; |
| 659 | if (!CBS_get_u8(©, &first_byte)) { |
| 660 | return 0; // INTEGERs may not be empty. |
| 661 | } |
| 662 | if (out_is_negative != NULL) { |
| 663 | *out_is_negative = (first_byte & 0x80) != 0; |
| 664 | } |
| 665 | if (!CBS_get_u8(©, &second_byte)) { |
| 666 | return 1; // One byte INTEGERs are always minimal. |
| 667 | } |
| 668 | if ((first_byte == 0x00 && (second_byte & 0x80) == 0) || |
| 669 | (first_byte == 0xff && (second_byte & 0x80) != 0)) { |
| 670 | return 0; // The value is minimal iff the first 9 bits are not all equal. |
| 671 | } |
| 672 | return 1; |
| 673 | } |
| 674 | |
| 675 | int CBS_is_unsigned_asn1_integer(const CBS *cbs) { |
| 676 | int is_negative; |
| 677 | return CBS_is_valid_asn1_integer(cbs, &is_negative) && !is_negative; |
| 678 | } |
| 679 | |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 680 | static int add_decimal(CBB *out, uint64_t v) { |
| 681 | char buf[DECIMAL_SIZE(uint64_t) + 1]; |
David Benjamin | 23d6e4c | 2023-07-15 02:44:54 -0400 | [diff] [blame] | 682 | snprintf(buf, sizeof(buf), "%" PRIu64, v); |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 683 | return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf)); |
| 684 | } |
| 685 | |
David Benjamin | 898de8d | 2023-03-11 12:26:37 -0500 | [diff] [blame] | 686 | int CBS_is_valid_asn1_oid(const CBS *cbs) { |
| 687 | if (CBS_len(cbs) == 0) { |
| 688 | return 0; // OID encodings cannot be empty. |
| 689 | } |
| 690 | |
| 691 | CBS copy = *cbs; |
| 692 | uint8_t v, prev = 0; |
| 693 | while (CBS_get_u8(©, &v)) { |
| 694 | // OID encodings are a sequence of minimally-encoded base-128 integers (see |
| 695 | // |parse_base128_integer|). If |prev|'s MSB was clear, it was the last byte |
| 696 | // of an integer (or |v| is the first byte). |v| is then the first byte of |
| 697 | // the next integer. If first byte of an integer is 0x80, it is not |
| 698 | // minimally-encoded. |
| 699 | if ((prev & 0x80) == 0 && v == 0x80) { |
| 700 | return 0; |
| 701 | } |
| 702 | prev = v; |
| 703 | } |
| 704 | |
| 705 | // The last byte should must end an integer encoding. |
| 706 | return (prev & 0x80) == 0; |
| 707 | } |
| 708 | |
David Benjamin | 095b6c9 | 2017-11-29 17:17:41 -0500 | [diff] [blame] | 709 | char *CBS_asn1_oid_to_text(const CBS *cbs) { |
| 710 | CBB cbb; |
| 711 | if (!CBB_init(&cbb, 32)) { |
| 712 | goto err; |
| 713 | } |
| 714 | |
| 715 | CBS copy = *cbs; |
| 716 | // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2. |
| 717 | uint64_t v; |
| 718 | if (!parse_base128_integer(©, &v)) { |
| 719 | goto err; |
| 720 | } |
| 721 | |
| 722 | if (v >= 80) { |
| 723 | if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) || |
| 724 | !add_decimal(&cbb, v - 80)) { |
| 725 | goto err; |
| 726 | } |
| 727 | } else if (!add_decimal(&cbb, v / 40) || |
| 728 | !CBB_add_u8(&cbb, '.') || |
| 729 | !add_decimal(&cbb, v % 40)) { |
| 730 | goto err; |
| 731 | } |
| 732 | |
| 733 | while (CBS_len(©) != 0) { |
| 734 | if (!parse_base128_integer(©, &v) || |
| 735 | !CBB_add_u8(&cbb, '.') || |
| 736 | !add_decimal(&cbb, v)) { |
| 737 | goto err; |
| 738 | } |
| 739 | } |
| 740 | |
| 741 | uint8_t *txt; |
| 742 | size_t txt_len; |
| 743 | if (!CBB_add_u8(&cbb, '\0') || |
| 744 | !CBB_finish(&cbb, &txt, &txt_len)) { |
| 745 | goto err; |
| 746 | } |
| 747 | |
| 748 | return (char *)txt; |
| 749 | |
| 750 | err: |
| 751 | CBB_cleanup(&cbb); |
| 752 | return NULL; |
| 753 | } |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 754 | |
| 755 | static int cbs_get_two_digits(CBS *cbs, int *out) { |
| 756 | uint8_t first_digit, second_digit; |
| 757 | if (!CBS_get_u8(cbs, &first_digit)) { |
| 758 | return 0; |
| 759 | } |
Bob Beck | f86a63c | 2023-01-30 12:17:39 -0700 | [diff] [blame] | 760 | if (!OPENSSL_isdigit(first_digit)) { |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 761 | return 0; |
| 762 | } |
| 763 | if (!CBS_get_u8(cbs, &second_digit)) { |
| 764 | return 0; |
| 765 | } |
Bob Beck | f86a63c | 2023-01-30 12:17:39 -0700 | [diff] [blame] | 766 | if (!OPENSSL_isdigit(second_digit)) { |
Bob Beck | 652464e | 2022-06-16 10:49:00 -0600 | [diff] [blame] | 767 | return 0; |
| 768 | } |
| 769 | *out = (first_digit - '0') * 10 + (second_digit - '0'); |
| 770 | return 1; |
| 771 | } |
| 772 | |
| 773 | static int is_valid_day(int year, int month, int day) { |
| 774 | if (day < 1) { |
| 775 | return 0; |
| 776 | } |
| 777 | switch (month) { |
| 778 | case 1: |
| 779 | case 3: |
| 780 | case 5: |
| 781 | case 7: |
| 782 | case 8: |
| 783 | case 10: |
| 784 | case 12: |
| 785 | return day <= 31; |
| 786 | case 4: |
| 787 | case 6: |
| 788 | case 9: |
| 789 | case 11: |
| 790 | return day <= 30; |
| 791 | case 2: |
| 792 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { |
| 793 | return day <= 29; |
| 794 | } else { |
| 795 | return day <= 28; |
| 796 | } |
| 797 | default: |
| 798 | return 0; |
| 799 | } |
| 800 | } |
| 801 | |
| 802 | static int CBS_parse_rfc5280_time_internal(const CBS *cbs, int is_gentime, |
| 803 | int allow_timezone_offset, |
| 804 | struct tm *out_tm) { |
| 805 | int year, month, day, hour, min, sec, tmp; |
| 806 | CBS copy = *cbs; |
| 807 | uint8_t tz; |
| 808 | |
| 809 | if (is_gentime) { |
| 810 | if (!cbs_get_two_digits(©, &tmp)) { |
| 811 | return 0; |
| 812 | } |
| 813 | year = tmp * 100; |
| 814 | if (!cbs_get_two_digits(©, &tmp)) { |
| 815 | return 0; |
| 816 | } |
| 817 | year += tmp; |
| 818 | } else { |
| 819 | year = 1900; |
| 820 | if (!cbs_get_two_digits(©, &tmp)) { |
| 821 | return 0; |
| 822 | } |
| 823 | year += tmp; |
| 824 | if (year < 1950) { |
| 825 | year += 100; |
| 826 | } |
| 827 | if (year >= 2050) { |
| 828 | return 0; // A Generalized time must be used. |
| 829 | } |
| 830 | } |
| 831 | if (!cbs_get_two_digits(©, &month) || month < 1 || |
| 832 | month > 12 || // Reject invalid months. |
| 833 | !cbs_get_two_digits(©, &day) || |
| 834 | !is_valid_day(year, month, day) || // Reject invalid days. |
| 835 | !cbs_get_two_digits(©, &hour) || |
| 836 | hour > 23 || // Reject invalid hours. |
| 837 | !cbs_get_two_digits(©, &min) || |
| 838 | min > 59 || // Reject invalid minutes. |
| 839 | !cbs_get_two_digits(©, &sec) || sec > 59 || !CBS_get_u8(©, &tz)) { |
| 840 | return 0; |
| 841 | } |
| 842 | |
| 843 | int offset_sign = 0; |
| 844 | switch (tz) { |
| 845 | case 'Z': |
| 846 | break; // We correctly have 'Z' on the end as per spec. |
| 847 | case '+': |
| 848 | offset_sign = 1; |
| 849 | break; // Should not be allowed per RFC 5280. |
| 850 | case '-': |
| 851 | offset_sign = -1; |
| 852 | break; // Should not be allowed per RFC 5280. |
| 853 | default: |
| 854 | return 0; // Reject anything else after the time. |
| 855 | } |
| 856 | |
| 857 | // If allow_timezone_offset is non-zero, allow for a four digit timezone |
| 858 | // offset to be specified even though this is not allowed by RFC 5280. We are |
| 859 | // permissive of this for UTCTimes due to the unfortunate existence of |
| 860 | // artisinally rolled long lived certificates that were baked into places that |
| 861 | // are now difficult to change. These certificates were generated with the |
| 862 | // 'openssl' command that permissively allowed the creation of certificates |
| 863 | // with notBefore and notAfter times specified as strings for direct |
| 864 | // certificate inclusion on the command line. For context see cl/237068815. |
| 865 | // |
| 866 | // TODO(bbe): This has been expunged from public web-pki as the ecosystem has |
| 867 | // managed to encourage CA compliance with standards. We should find a way to |
| 868 | // get rid of this or make it off by default. |
| 869 | int offset_seconds = 0; |
| 870 | if (offset_sign != 0) { |
| 871 | if (!allow_timezone_offset) { |
| 872 | return 0; |
| 873 | } |
| 874 | int offset_hours, offset_minutes; |
| 875 | if (!cbs_get_two_digits(©, &offset_hours) || |
| 876 | offset_hours > 23 || // Reject invalid hours. |
| 877 | !cbs_get_two_digits(©, &offset_minutes) || |
| 878 | offset_minutes > 59) { // Reject invalid minutes. |
| 879 | return 0; |
| 880 | } |
| 881 | offset_seconds = offset_sign * (offset_hours * 3600 + offset_minutes * 60); |
| 882 | } |
| 883 | |
| 884 | if (CBS_len(©) != 0) { |
| 885 | return 0; // Reject invalid lengths. |
| 886 | } |
| 887 | |
| 888 | if (out_tm != NULL) { |
| 889 | // Fill in the tm fields corresponding to what we validated. |
| 890 | out_tm->tm_year = year - 1900; |
| 891 | out_tm->tm_mon = month - 1; |
| 892 | out_tm->tm_mday = day; |
| 893 | out_tm->tm_hour = hour; |
| 894 | out_tm->tm_min = min; |
| 895 | out_tm->tm_sec = sec; |
| 896 | if (offset_seconds && !OPENSSL_gmtime_adj(out_tm, 0, offset_seconds)) { |
| 897 | return 0; |
| 898 | } |
| 899 | } |
| 900 | return 1; |
| 901 | } |
| 902 | |
| 903 | int CBS_parse_generalized_time(const CBS *cbs, struct tm *out_tm, |
| 904 | int allow_timezone_offset) { |
| 905 | return CBS_parse_rfc5280_time_internal(cbs, 1, allow_timezone_offset, out_tm); |
| 906 | } |
| 907 | |
| 908 | int CBS_parse_utc_time(const CBS *cbs, struct tm *out_tm, |
| 909 | int allow_timezone_offset) { |
| 910 | return CBS_parse_rfc5280_time_internal(cbs, 0, allow_timezone_offset, out_tm); |
| 911 | } |