1 /* 2 * Copyright 2017-2026 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <openssl/rand.h> 14 #include <openssl/asn1.h> 15 #include <openssl/asn1t.h> 16 #include <openssl/obj_mac.h> 17 #include <openssl/bio.h> 18 #include <openssl/buffer.h> 19 #include <openssl/err.h> 20 #include "internal/numbers.h" 21 #include "internal/asn1.h" 22 #include "testutil.h" 23 24 #ifdef __GNUC__ 25 #pragma GCC diagnostic ignored "-Wunused-function" 26 #endif 27 #ifdef __clang__ 28 #pragma clang diagnostic ignored "-Wunused-function" 29 #endif 30 31 /* Badly coded ASN.1 INTEGER zero wrapped in a sequence */ 32 static unsigned char t_invalid_zero[] = { 33 0x30, 0x02, /* SEQUENCE tag + length */ 34 0x02, 0x00 /* INTEGER tag + length */ 35 }; 36 37 #ifndef OPENSSL_NO_DEPRECATED_3_0 38 /* LONG case ************************************************************* */ 39 40 typedef struct { 41 long test_long; 42 } ASN1_LONG_DATA; 43 44 ASN1_SEQUENCE(ASN1_LONG_DATA) = { 45 ASN1_EMBED(ASN1_LONG_DATA, test_long, LONG), 46 } static_ASN1_SEQUENCE_END(ASN1_LONG_DATA) 47 48 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_LONG_DATA) 49 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_LONG_DATA) 50 51 static int test_long(void) 52 { 53 const unsigned char *p = t_invalid_zero; 54 ASN1_LONG_DATA *dectst = d2i_ASN1_LONG_DATA(NULL, &p, sizeof(t_invalid_zero)); 55 56 if (dectst == NULL) 57 return 0; /* Fail */ 58 59 ASN1_LONG_DATA_free(dectst); 60 return 1; 61 } 62 #endif 63 64 /* INT32 case ************************************************************* */ 65 66 typedef struct { 67 int32_t test_int32; 68 } ASN1_INT32_DATA; 69 70 ASN1_SEQUENCE(ASN1_INT32_DATA) = { 71 ASN1_EMBED(ASN1_INT32_DATA, test_int32, INT32), 72 } static_ASN1_SEQUENCE_END(ASN1_INT32_DATA) 73 74 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_INT32_DATA) 75 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_INT32_DATA) 76 77 static int test_int32(void) 78 { 79 const unsigned char *p = t_invalid_zero; 80 ASN1_INT32_DATA *dectst = d2i_ASN1_INT32_DATA(NULL, &p, sizeof(t_invalid_zero)); 81 82 if (dectst == NULL) 83 return 0; /* Fail */ 84 85 ASN1_INT32_DATA_free(dectst); 86 return 1; 87 } 88 89 /* UINT32 case ************************************************************* */ 90 91 typedef struct { 92 uint32_t test_uint32; 93 } ASN1_UINT32_DATA; 94 95 ASN1_SEQUENCE(ASN1_UINT32_DATA) = { 96 ASN1_EMBED(ASN1_UINT32_DATA, test_uint32, UINT32), 97 } static_ASN1_SEQUENCE_END(ASN1_UINT32_DATA) 98 99 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_UINT32_DATA) 100 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_UINT32_DATA) 101 102 static int test_uint32(void) 103 { 104 const unsigned char *p = t_invalid_zero; 105 ASN1_UINT32_DATA *dectst = d2i_ASN1_UINT32_DATA(NULL, &p, sizeof(t_invalid_zero)); 106 107 if (dectst == NULL) 108 return 0; /* Fail */ 109 110 ASN1_UINT32_DATA_free(dectst); 111 return 1; 112 } 113 114 /* INT64 case ************************************************************* */ 115 116 typedef struct { 117 int64_t test_int64; 118 } ASN1_INT64_DATA; 119 120 ASN1_SEQUENCE(ASN1_INT64_DATA) = { 121 ASN1_EMBED(ASN1_INT64_DATA, test_int64, INT64), 122 } static_ASN1_SEQUENCE_END(ASN1_INT64_DATA) 123 124 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_INT64_DATA) 125 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_INT64_DATA) 126 127 static int test_int64(void) 128 { 129 const unsigned char *p = t_invalid_zero; 130 ASN1_INT64_DATA *dectst = d2i_ASN1_INT64_DATA(NULL, &p, sizeof(t_invalid_zero)); 131 132 if (dectst == NULL) 133 return 0; /* Fail */ 134 135 ASN1_INT64_DATA_free(dectst); 136 return 1; 137 } 138 139 /* UINT64 case ************************************************************* */ 140 141 typedef struct { 142 uint64_t test_uint64; 143 } ASN1_UINT64_DATA; 144 145 ASN1_SEQUENCE(ASN1_UINT64_DATA) = { 146 ASN1_EMBED(ASN1_UINT64_DATA, test_uint64, UINT64), 147 } static_ASN1_SEQUENCE_END(ASN1_UINT64_DATA) 148 149 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_UINT64_DATA) 150 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_UINT64_DATA) 151 152 static int test_uint64(void) 153 { 154 const unsigned char *p = t_invalid_zero; 155 ASN1_UINT64_DATA *dectst = d2i_ASN1_UINT64_DATA(NULL, &p, sizeof(t_invalid_zero)); 156 157 if (dectst == NULL) 158 return 0; /* Fail */ 159 160 ASN1_UINT64_DATA_free(dectst); 161 return 1; 162 } 163 164 /* GeneralizedTime underflow *********************************************** */ 165 166 static int test_gentime(void) 167 { 168 /* Underflowing GeneralizedTime 161208193400Z (YYMMDDHHMMSSZ) */ 169 const unsigned char der[] = { 170 0x18, 0x0d, 0x31, 0x36, 0x31, 0x32, 0x30, 0x38, 0x31, 0x39, 171 0x33, 0x34, 0x30, 0x30, 0x5a 172 }; 173 const unsigned char *p; 174 int der_len, rc = 1; 175 ASN1_GENERALIZEDTIME *gentime; 176 177 p = der; 178 der_len = sizeof(der); 179 gentime = d2i_ASN1_GENERALIZEDTIME(NULL, &p, der_len); 180 181 if (!TEST_ptr_null(gentime)) 182 rc = 0; /* fail */ 183 184 ASN1_GENERALIZEDTIME_free(gentime); 185 return rc; 186 } 187 188 /* UTCTime underflow ******************************************************* */ 189 190 static int test_utctime(void) 191 { 192 /* Underflowing UTCTime 0205104700Z (MMDDHHMMSSZ) */ 193 const unsigned char der[] = { 194 0x17, 0x0b, 0x30, 0x32, 0x30, 0x35, 0x31, 0x30, 0x34, 0x37, 195 0x30, 0x30, 0x5a 196 }; 197 const unsigned char *p; 198 int der_len, rc = 1; 199 ASN1_UTCTIME *utctime; 200 201 p = der; 202 der_len = sizeof(der); 203 utctime = d2i_ASN1_UTCTIME(NULL, &p, der_len); 204 205 if (!TEST_ptr_null(utctime)) 206 rc = 0; /* fail */ 207 208 ASN1_UTCTIME_free(utctime); 209 return rc; 210 } 211 212 /* Invalid template ******************************************************** */ 213 214 typedef struct { 215 ASN1_STRING *invalidDirString; 216 } INVALIDTEMPLATE; 217 218 ASN1_SEQUENCE(INVALIDTEMPLATE) = { 219 /* 220 * DirectoryString is a CHOICE type so it must use explicit tagging - 221 * but we deliberately use implicit here, which makes this template invalid. 222 */ 223 ASN1_IMP(INVALIDTEMPLATE, invalidDirString, DIRECTORYSTRING, 12) 224 } static_ASN1_SEQUENCE_END(INVALIDTEMPLATE) 225 226 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(INVALIDTEMPLATE) 227 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(INVALIDTEMPLATE) 228 229 /* Empty sequence for invalid template test */ 230 static unsigned char t_invalid_template[] = { 231 0x30, 0x03, /* SEQUENCE tag + length */ 232 0x0c, 0x01, 0x41 /* UTF8String, length 1, "A" */ 233 }; 234 235 static int test_invalid_template(void) 236 { 237 const unsigned char *p = t_invalid_template; 238 INVALIDTEMPLATE *tmp = d2i_INVALIDTEMPLATE(NULL, &p, 239 sizeof(t_invalid_template)); 240 241 /* We expect a NULL pointer return */ 242 if (TEST_ptr_null(tmp)) 243 return 1; 244 245 INVALIDTEMPLATE_free(tmp); 246 return 0; 247 } 248 249 static int test_reuse_asn1_object(void) 250 { 251 static unsigned char cn_der[] = { 0x06, 0x03, 0x55, 0x04, 0x06 }; 252 static unsigned char oid_der[] = { 253 0x06, 0x06, 0x2a, 0x03, 0x04, 0x05, 0x06, 0x07 254 }; 255 int ret = 0; 256 ASN1_OBJECT *obj; 257 unsigned char const *p = oid_der; 258 259 /* Create an object that owns dynamically allocated 'sn' and 'ln' fields */ 260 261 if (!TEST_ptr(obj = ASN1_OBJECT_create(NID_undef, cn_der, sizeof(cn_der), 262 "C", "countryName"))) 263 goto err; 264 /* reuse obj - this should not leak sn and ln */ 265 if (!TEST_ptr(d2i_ASN1_OBJECT(&obj, &p, sizeof(oid_der)))) 266 goto err; 267 ret = 1; 268 err: 269 ASN1_OBJECT_free(obj); 270 return ret; 271 } 272 273 /* 274 * A minimal, complete DER object: SEQUENCE { INTEGER 0 }. 275 * asn1_d2i_read_bio() should consume exactly these bytes. 276 */ 277 static const unsigned char one_obj[] = { 278 0x30, 0x03, /* SEQUENCE, length 3 */ 279 0x02, 0x01, 0x00 /* INTEGER 0 */ 280 }; 281 282 /* 283 * Reading concatenated DER objects from a BIO must stop cleanly at EOF: 284 * once the input is exhausted on an object boundary, asn1_d2i_read_bio() 285 * returns < 0 and must NOT leave an error on the queue. Callers that loop 286 * over concatenated values (e.g. CPython's ssl module loading the Windows 287 * certificate store via d2i_X509_bio()) rely on this to detect end-of-input; 288 * a spurious ASN1_R_NOT_ENOUGH_DATA there is reported as a fatal error. 289 */ 290 static int test_d2i_read_bio_clean_eof(void) 291 { 292 unsigned char two_objs[sizeof(one_obj) * 2]; 293 BIO *bio = NULL; 294 BUF_MEM *buf = NULL; 295 int ret = 0; 296 297 memcpy(two_objs, one_obj, sizeof(one_obj)); 298 memcpy(two_objs + sizeof(one_obj), one_obj, sizeof(one_obj)); 299 300 if (!TEST_ptr(bio = BIO_new_mem_buf(two_objs, sizeof(two_objs)))) 301 goto err; 302 ERR_clear_error(); 303 304 /* Both complete objects are read, one per call. */ 305 if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj))) 306 goto err; 307 BUF_MEM_free(buf); 308 buf = NULL; 309 if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj))) 310 goto err; 311 BUF_MEM_free(buf); 312 buf = NULL; 313 314 /* Clean EOF: failure return, but no error must be queued. */ 315 if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) 316 goto err; 317 if (!TEST_ulong_eq(ERR_peek_error(), 0)) 318 goto err; 319 320 ret = 1; 321 err: 322 BUF_MEM_free(buf); 323 BIO_free(bio); 324 return ret; 325 } 326 327 /* 328 * In contrast, hitting EOF in the middle of an object is genuine truncation 329 * and must still be reported as ASN1_R_NOT_ENOUGH_DATA. 330 */ 331 static int test_d2i_read_bio_truncated(void) 332 { 333 static const unsigned char truncated[] = { 334 0x30, 0x05, /* SEQUENCE claims 5 content bytes ... */ 335 0x02, 0x01 /* ... but only 2 are present */ 336 }; 337 BIO *bio = NULL; 338 BUF_MEM *buf = NULL; 339 unsigned long e; 340 int ret = 0; 341 342 if (!TEST_ptr(bio = BIO_new_mem_buf(truncated, sizeof(truncated)))) 343 goto err; 344 ERR_clear_error(); 345 346 if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) 347 goto err; 348 e = ERR_peek_last_error(); 349 if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) 350 || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) 351 goto err; 352 353 ret = 1; 354 err: 355 BUF_MEM_free(buf); 356 BIO_free(bio); 357 return ret; 358 } 359 360 /* 361 * An EOF reached while still inside an indefinite-length constructed value, 362 * before its end-of-contents octets, is truncation too (not a clean boundary), 363 * so it must also report ASN1_R_NOT_ENOUGH_DATA rather than an empty queue. 364 */ 365 static int test_d2i_read_bio_indefinite_truncated(void) 366 { 367 /* SEQUENCE (indefinite) { INTEGER 0 } with the 00 00 EOC missing */ 368 static const unsigned char truncated_indefinite[] = { 369 0x30, 0x80, /* SEQUENCE, indefinite length */ 370 0x02, 0x01, 0x00 /* INTEGER 0; no end-of-contents octets follow */ 371 }; 372 BIO *bio = NULL; 373 BUF_MEM *buf = NULL; 374 unsigned long e; 375 int ret = 0; 376 377 bio = BIO_new_mem_buf(truncated_indefinite, sizeof(truncated_indefinite)); 378 if (!TEST_ptr(bio)) 379 goto err; 380 ERR_clear_error(); 381 382 if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) 383 goto err; 384 e = ERR_peek_last_error(); 385 if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) 386 || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) 387 goto err; 388 389 ret = 1; 390 err: 391 BUF_MEM_free(buf); 392 BIO_free(bio); 393 return ret; 394 } 395 396 /* 397 * An EOF reached part-way through an object's header, with some header bytes 398 * already buffered, is truncation as well. This exercises the "diff != 0" arm 399 * of the header-read check (distinct from the body read handled elsewhere). 400 */ 401 static int test_d2i_read_bio_partial_header(void) 402 { 403 /* SEQUENCE with a 2-byte long-form length, but only one length byte given */ 404 static const unsigned char partial_header[] = { 405 0x30, 0x82, 0x01 /* SEQUENCE, length declared as 2 bytes, 1 present */ 406 }; 407 BIO *bio = NULL; 408 BUF_MEM *buf = NULL; 409 unsigned long e; 410 int ret = 0; 411 412 if (!TEST_ptr(bio = BIO_new_mem_buf(partial_header, sizeof(partial_header)))) 413 goto err; 414 ERR_clear_error(); 415 416 if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) 417 goto err; 418 e = ERR_peek_last_error(); 419 if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) 420 || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) 421 goto err; 422 423 ret = 1; 424 err: 425 BUF_MEM_free(buf); 426 BIO_free(bio); 427 return ret; 428 } 429 430 int setup_tests(void) 431 { 432 #ifndef OPENSSL_NO_DEPRECATED_3_0 433 ADD_TEST(test_long); 434 #endif 435 ADD_TEST(test_int32); 436 ADD_TEST(test_uint32); 437 ADD_TEST(test_int64); 438 ADD_TEST(test_uint64); 439 ADD_TEST(test_gentime); 440 ADD_TEST(test_utctime); 441 ADD_TEST(test_invalid_template); 442 ADD_TEST(test_reuse_asn1_object); 443 ADD_TEST(test_d2i_read_bio_clean_eof); 444 ADD_TEST(test_d2i_read_bio_truncated); 445 ADD_TEST(test_d2i_read_bio_indefinite_truncated); 446 ADD_TEST(test_d2i_read_bio_partial_header); 447 return 1; 448 } 449