1 /* 2 * Copyright 2017-2021 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 /* Tests for X509 time functions */ 11 12 #include <string.h> 13 #include <time.h> 14 15 #include <openssl/asn1.h> 16 #include <openssl/x509.h> 17 #include "testutil.h" 18 #include "internal/nelem.h" 19 20 typedef struct { 21 const char *data; 22 int type; 23 time_t cmp_time; 24 /* -1 if asn1_time <= cmp_time, 1 if asn1_time > cmp_time, 0 if error. */ 25 int expected; 26 } TESTDATA; 27 28 typedef struct { 29 const char *data; 30 /* 0 for check-only mode, 1 for set-string mode */ 31 int set_string; 32 /* 0 for error, 1 if succeed */ 33 int expected; 34 /* 35 * The following 2 fields are ignored if set_string field is set to '0' 36 * (in check only mode). 37 * 38 * But they can still be ignored explicitly in set-string mode by: 39 * setting -1 to expected_type and setting NULL to expected_string. 40 * 41 * It's useful in a case of set-string mode but the expected result 42 * is a 'parsing error'. 43 */ 44 int expected_type; 45 const char *expected_string; 46 } TESTDATA_FORMAT; 47 48 /* 49 * Actually, the "loose" mode has been tested in 50 * those time-compare-cases, so we may not test it again. 51 */ 52 static TESTDATA_FORMAT x509_format_tests[] = { 53 /* GeneralizedTime */ 54 { 55 /* good format, check only */ 56 "20170217180105Z", 0, 1, -1, NULL, 57 }, 58 { 59 /* not leap year, check only */ 60 "20170229180105Z", 0, 0, -1, NULL, 61 }, 62 { 63 /* leap year, check only */ 64 "20160229180105Z", 0, 1, -1, NULL, 65 }, 66 { 67 /* SS is missing, check only */ 68 "201702171801Z", 0, 0, -1, NULL, 69 }, 70 { 71 /* fractional seconds, check only */ 72 "20170217180105.001Z", 0, 0, -1, NULL, 73 }, 74 { 75 /* time zone, check only */ 76 "20170217180105+0800", 0, 0, -1, NULL, 77 }, 78 { 79 /* SS is missing, set string */ 80 "201702171801Z", 1, 0, -1, NULL, 81 }, 82 { 83 /* fractional seconds, set string */ 84 "20170217180105.001Z", 1, 0, -1, NULL, 85 }, 86 { 87 /* time zone, set string */ 88 "20170217180105+0800", 1, 0, -1, NULL, 89 }, 90 { 91 /* good format, check returned 'turned' string */ 92 "20170217180154Z", 1, 1, V_ASN1_UTCTIME, "170217180154Z", 93 }, 94 { 95 /* good format, check returned string */ 96 "20510217180154Z", 1, 1, V_ASN1_GENERALIZEDTIME, "20510217180154Z", 97 }, 98 { 99 /* good format but out of UTC range, check returned string */ 100 "19230419180154Z", 1, 1, V_ASN1_GENERALIZEDTIME, "19230419180154Z", 101 }, 102 /* UTC */ 103 { 104 /* SS is missing, check only */ 105 "1702171801Z", 0, 0, -1, NULL, 106 }, 107 { 108 /* not leap year, check only */ 109 "050229180101Z", 0, 0, -1, NULL, 110 }, 111 { 112 /* leap year, check only */ 113 "040229180101Z", 0, 1, -1, NULL, 114 }, 115 { 116 /* time zone, check only */ 117 "170217180154+0800", 0, 0, -1, NULL, 118 }, 119 { 120 /* SS is missing, set string */ 121 "1702171801Z", 1, 0, -1, NULL, 122 }, 123 { 124 /* time zone, set string */ 125 "170217180154+0800", 1, 0, -1, NULL, 126 }, 127 { 128 /* 2017, good format, check returned string */ 129 "170217180154Z", 1, 1, V_ASN1_UTCTIME, "170217180154Z", 130 }, 131 { 132 /* 1998, good format, check returned string */ 133 "981223180154Z", 1, 1, V_ASN1_UTCTIME, "981223180154Z", 134 }, 135 }; 136 137 static TESTDATA x509_cmp_tests[] = { 138 { 139 "20170217180154Z", V_ASN1_GENERALIZEDTIME, 140 /* The same in seconds since epoch. */ 141 1487354514, -1, 142 }, 143 { 144 "20170217180154Z", V_ASN1_GENERALIZEDTIME, 145 /* One second more. */ 146 1487354515, -1, 147 }, 148 { 149 "20170217180154Z", V_ASN1_GENERALIZEDTIME, 150 /* One second less. */ 151 1487354513, 1, 152 }, 153 /* Same as UTC time. */ 154 { 155 "170217180154Z", V_ASN1_UTCTIME, 156 /* The same in seconds since epoch. */ 157 1487354514, -1, 158 }, 159 { 160 "170217180154Z", V_ASN1_UTCTIME, 161 /* One second more. */ 162 1487354515, -1, 163 }, 164 { 165 "170217180154Z", V_ASN1_UTCTIME, 166 /* One second less. */ 167 1487354513, 1, 168 }, 169 /* UTCTime from the 20th century. */ 170 { 171 "990217180154Z", V_ASN1_UTCTIME, 172 /* The same in seconds since epoch. */ 173 919274514, -1, 174 }, 175 { 176 "990217180154Z", V_ASN1_UTCTIME, 177 /* One second more. */ 178 919274515, -1, 179 }, 180 { 181 "990217180154Z", V_ASN1_UTCTIME, 182 /* One second less. */ 183 919274513, 1, 184 }, 185 /* Various invalid formats. */ 186 { 187 /* No trailing Z. */ 188 "20170217180154", V_ASN1_GENERALIZEDTIME, 0, 0, 189 }, 190 { 191 /* No trailing Z, UTCTime. */ 192 "170217180154", V_ASN1_UTCTIME, 0, 0, 193 }, 194 { 195 /* No seconds. */ 196 "201702171801Z", V_ASN1_GENERALIZEDTIME, 0, 0, 197 }, 198 { 199 /* No seconds, UTCTime. */ 200 "1702171801Z", V_ASN1_UTCTIME, 0, 0, 201 }, 202 { 203 /* Fractional seconds. */ 204 "20170217180154.001Z", V_ASN1_GENERALIZEDTIME, 0, 0, 205 }, 206 { 207 /* Fractional seconds, UTCTime. */ 208 "170217180154.001Z", V_ASN1_UTCTIME, 0, 0, 209 }, 210 { 211 /* Timezone offset. */ 212 "20170217180154+0100", V_ASN1_GENERALIZEDTIME, 0, 0, 213 }, 214 { 215 /* Timezone offset, UTCTime. */ 216 "170217180154+0100", V_ASN1_UTCTIME, 0, 0, 217 }, 218 { 219 /* Extra digits. */ 220 "2017021718015400Z", V_ASN1_GENERALIZEDTIME, 0, 0, 221 }, 222 { 223 /* Extra digits, UTCTime. */ 224 "17021718015400Z", V_ASN1_UTCTIME, 0, 0, 225 }, 226 { 227 /* Non-digits. */ 228 "2017021718015aZ", V_ASN1_GENERALIZEDTIME, 0, 0, 229 }, 230 { 231 /* Non-digits, UTCTime. */ 232 "17021718015aZ", V_ASN1_UTCTIME, 0, 0, 233 }, 234 { 235 /* Trailing garbage. */ 236 "20170217180154Zlongtrailinggarbage", V_ASN1_GENERALIZEDTIME, 0, 0, 237 }, 238 { 239 /* Trailing garbage, UTCTime. */ 240 "170217180154Zlongtrailinggarbage", V_ASN1_UTCTIME, 0, 0, 241 }, 242 { 243 /* Swapped type. */ 244 "20170217180154Z", V_ASN1_UTCTIME, 0, 0, 245 }, 246 { 247 /* Swapped type. */ 248 "170217180154Z", V_ASN1_GENERALIZEDTIME, 0, 0, 249 }, 250 { 251 /* Bad type. */ 252 "20170217180154Z", V_ASN1_OCTET_STRING, 0, 0, 253 }, 254 }; 255 256 static int test_x509_cmp_time(int idx) 257 { 258 ASN1_TIME t; 259 int result; 260 261 memset(&t, 0, sizeof(t)); 262 t.type = x509_cmp_tests[idx].type; 263 t.data = (unsigned char*)(x509_cmp_tests[idx].data); 264 t.length = strlen(x509_cmp_tests[idx].data); 265 t.flags = 0; 266 267 result = X509_cmp_time(&t, &x509_cmp_tests[idx].cmp_time); 268 if (!TEST_int_eq(result, x509_cmp_tests[idx].expected)) { 269 TEST_info("test_x509_cmp_time(%d) failed: expected %d, got %d\n", 270 idx, x509_cmp_tests[idx].expected, result); 271 return 0; 272 } 273 return 1; 274 } 275 276 static int test_x509_cmp_time_current(void) 277 { 278 time_t now = time(NULL); 279 /* Pick a day earlier and later, relative to any system clock. */ 280 ASN1_TIME *asn1_before = NULL, *asn1_after = NULL; 281 int cmp_result, failed = 0; 282 283 asn1_before = ASN1_TIME_adj(NULL, now, -1, 0); 284 asn1_after = ASN1_TIME_adj(NULL, now, 1, 0); 285 286 cmp_result = X509_cmp_time(asn1_before, NULL); 287 if (!TEST_int_eq(cmp_result, -1)) 288 failed = 1; 289 290 cmp_result = X509_cmp_time(asn1_after, NULL); 291 if (!TEST_int_eq(cmp_result, 1)) 292 failed = 1; 293 294 ASN1_TIME_free(asn1_before); 295 ASN1_TIME_free(asn1_after); 296 297 return failed == 0; 298 } 299 300 static int test_X509_cmp_timeframe_vpm(const X509_VERIFY_PARAM *vpm, 301 ASN1_TIME *asn1_before, 302 ASN1_TIME *asn1_mid, 303 ASN1_TIME *asn1_after) 304 { 305 int always_0 = vpm != NULL 306 && (X509_VERIFY_PARAM_get_flags(vpm) & X509_V_FLAG_USE_CHECK_TIME) == 0 307 && (X509_VERIFY_PARAM_get_flags(vpm) & X509_V_FLAG_NO_CHECK_TIME) != 0; 308 309 return asn1_before != NULL && asn1_mid != NULL && asn1_after != NULL 310 && TEST_int_eq(X509_cmp_timeframe(vpm, asn1_before, asn1_after), 0) 311 && TEST_int_eq(X509_cmp_timeframe(vpm, asn1_before, NULL), 0) 312 && TEST_int_eq(X509_cmp_timeframe(vpm, NULL, asn1_after), 0) 313 && TEST_int_eq(X509_cmp_timeframe(vpm, NULL, NULL), 0) 314 && TEST_int_eq(X509_cmp_timeframe(vpm, asn1_after, asn1_after), 315 always_0 ? 0 : -1) 316 && TEST_int_eq(X509_cmp_timeframe(vpm, asn1_before, asn1_before), 317 always_0 ? 0 : 1) 318 && TEST_int_eq(X509_cmp_timeframe(vpm, asn1_after, asn1_before), 319 always_0 ? 0 : 1); 320 } 321 322 static int test_X509_cmp_timeframe(void) 323 { 324 time_t now = time(NULL); 325 ASN1_TIME *asn1_mid = ASN1_TIME_adj(NULL, now, 0, 0); 326 /* Pick a day earlier and later, relative to any system clock. */ 327 ASN1_TIME *asn1_before = ASN1_TIME_adj(NULL, now, -1, 0); 328 ASN1_TIME *asn1_after = ASN1_TIME_adj(NULL, now, 1, 0); 329 X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new(); 330 int res = 0; 331 332 if (vpm == NULL) 333 goto finish; 334 res = test_X509_cmp_timeframe_vpm(NULL, asn1_before, asn1_mid, asn1_after) 335 && test_X509_cmp_timeframe_vpm(vpm, asn1_before, asn1_mid, asn1_after); 336 337 X509_VERIFY_PARAM_set_time(vpm, now); 338 res = res 339 && test_X509_cmp_timeframe_vpm(vpm, asn1_before, asn1_mid, asn1_after) 340 && X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_CHECK_TIME) 341 && test_X509_cmp_timeframe_vpm(vpm, asn1_before, asn1_mid, asn1_after); 342 343 X509_VERIFY_PARAM_free(vpm); 344 finish: 345 ASN1_TIME_free(asn1_mid); 346 ASN1_TIME_free(asn1_before); 347 ASN1_TIME_free(asn1_after); 348 349 return res; 350 } 351 352 static int test_x509_time(int idx) 353 { 354 ASN1_TIME *t = NULL; 355 int result, rv = 0; 356 357 if (x509_format_tests[idx].set_string) { 358 /* set-string mode */ 359 t = ASN1_TIME_new(); 360 if (t == NULL) { 361 TEST_info("test_x509_time(%d) failed: internal error\n", idx); 362 return 0; 363 } 364 } 365 366 result = ASN1_TIME_set_string_X509(t, x509_format_tests[idx].data); 367 /* time string parsing result is always checked against what's expected */ 368 if (!TEST_int_eq(result, x509_format_tests[idx].expected)) { 369 TEST_info("test_x509_time(%d) failed: expected %d, got %d\n", 370 idx, x509_format_tests[idx].expected, result); 371 goto out; 372 } 373 374 /* if t is not NULL but expected_type is ignored(-1), it is an 'OK' case */ 375 if (t != NULL && x509_format_tests[idx].expected_type != -1) { 376 if (!TEST_int_eq(t->type, x509_format_tests[idx].expected_type)) { 377 TEST_info("test_x509_time(%d) failed: expected_type %d, got %d\n", 378 idx, x509_format_tests[idx].expected_type, t->type); 379 goto out; 380 } 381 } 382 383 /* if t is not NULL but expected_string is NULL, it is an 'OK' case too */ 384 if (t != NULL && x509_format_tests[idx].expected_string) { 385 if (!TEST_mem_eq((const char *)t->data, t->length, 386 x509_format_tests[idx].expected_string, 387 strlen(x509_format_tests[idx].expected_string))) { 388 TEST_info("test_x509_time(%d) failed: expected_string %s, got %.*s\n", 389 idx, x509_format_tests[idx].expected_string, t->length, 390 t->data); 391 goto out; 392 } 393 } 394 395 rv = 1; 396 out: 397 if (t != NULL) 398 ASN1_TIME_free(t); 399 return rv; 400 } 401 402 static const struct { 403 int y, m, d; 404 int yd, wd; 405 } day_of_week_tests[] = { 406 /*YYYY MM DD DoY DoW */ 407 { 1900, 1, 1, 0, 1 }, 408 { 1900, 2, 28, 58, 3 }, 409 { 1900, 3, 1, 59, 4 }, 410 { 1900, 12, 31, 364, 1 }, 411 { 1901, 1, 1, 0, 2 }, 412 { 1970, 1, 1, 0, 4 }, 413 { 1999, 1, 10, 9, 0 }, 414 { 1999, 12, 31, 364, 5 }, 415 { 2000, 1, 1, 0, 6 }, 416 { 2000, 2, 28, 58, 1 }, 417 { 2000, 2, 29, 59, 2 }, 418 { 2000, 3, 1, 60, 3 }, 419 { 2000, 12, 31, 365, 0 }, 420 { 2001, 1, 1, 0, 1 }, 421 { 2008, 1, 1, 0, 2 }, 422 { 2008, 2, 28, 58, 4 }, 423 { 2008, 2, 29, 59, 5 }, 424 { 2008, 3, 1, 60, 6 }, 425 { 2008, 12, 31, 365, 3 }, 426 { 2009, 1, 1, 0, 4 }, 427 { 2011, 1, 1, 0, 6 }, 428 { 2011, 2, 28, 58, 1 }, 429 { 2011, 3, 1, 59, 2 }, 430 { 2011, 12, 31, 364, 6 }, 431 { 2012, 1, 1, 0, 0 }, 432 { 2019, 1, 2, 1, 3 }, 433 { 2019, 2, 2, 32, 6 }, 434 { 2019, 3, 2, 60, 6 }, 435 { 2019, 4, 2, 91, 2 }, 436 { 2019, 5, 2, 121, 4 }, 437 { 2019, 6, 2, 152, 0 }, 438 { 2019, 7, 2, 182, 2 }, 439 { 2019, 8, 2, 213, 5 }, 440 { 2019, 9, 2, 244, 1 }, 441 { 2019, 10, 2, 274, 3 }, 442 { 2019, 11, 2, 305, 6 }, 443 { 2019, 12, 2, 335, 1 }, 444 { 2020, 1, 2, 1, 4 }, 445 { 2020, 2, 2, 32, 0 }, 446 { 2020, 3, 2, 61, 1 }, 447 { 2020, 4, 2, 92, 4 }, 448 { 2020, 5, 2, 122, 6 }, 449 { 2020, 6, 2, 153, 2 }, 450 { 2020, 7, 2, 183, 4 }, 451 { 2020, 8, 2, 214, 0 }, 452 { 2020, 9, 2, 245, 3 }, 453 { 2020, 10, 2, 275, 5 }, 454 { 2020, 11, 2, 306, 1 }, 455 { 2020, 12, 2, 336, 3 } 456 }; 457 458 static int test_days(int n) 459 { 460 char d[16]; 461 ASN1_TIME *a = NULL; 462 struct tm t; 463 int r; 464 465 BIO_snprintf(d, sizeof(d), "%04d%02d%02d050505Z", 466 day_of_week_tests[n].y, day_of_week_tests[n].m, 467 day_of_week_tests[n].d); 468 469 if (!TEST_ptr(a = ASN1_TIME_new())) 470 return 0; 471 472 r = TEST_true(ASN1_TIME_set_string(a, d)) 473 && TEST_true(ASN1_TIME_to_tm(a, &t)) 474 && TEST_int_eq(t.tm_yday, day_of_week_tests[n].yd) 475 && TEST_int_eq(t.tm_wday, day_of_week_tests[n].wd); 476 477 ASN1_TIME_free(a); 478 return r; 479 } 480 481 #define construct_asn1_time(s, t, e) \ 482 { { sizeof(s) - 1, t, (unsigned char*)s, 0 }, e } 483 484 static const struct { 485 ASN1_TIME asn1; 486 const char *readable; 487 } x509_print_tests_rfc_822 [] = { 488 /* Generalized Time */ 489 construct_asn1_time("20170731222050Z", V_ASN1_GENERALIZEDTIME, 490 "Jul 31 22:20:50 2017 GMT"), 491 /* Generalized Time, no seconds */ 492 construct_asn1_time("201707312220Z", V_ASN1_GENERALIZEDTIME, 493 "Jul 31 22:20:00 2017 GMT"), 494 /* Generalized Time, fractional seconds (3 digits) */ 495 construct_asn1_time("20170731222050.123Z", V_ASN1_GENERALIZEDTIME, 496 "Jul 31 22:20:50.123 2017 GMT"), 497 /* Generalized Time, fractional seconds (1 digit) */ 498 construct_asn1_time("20170731222050.1Z", V_ASN1_GENERALIZEDTIME, 499 "Jul 31 22:20:50.1 2017 GMT"), 500 /* Generalized Time, fractional seconds (0 digit) */ 501 construct_asn1_time("20170731222050.Z", V_ASN1_GENERALIZEDTIME, 502 "Bad time value"), 503 /* UTC Time */ 504 construct_asn1_time("170731222050Z", V_ASN1_UTCTIME, 505 "Jul 31 22:20:50 2017 GMT"), 506 /* UTC Time, no seconds */ 507 construct_asn1_time("1707312220Z", V_ASN1_UTCTIME, 508 "Jul 31 22:20:00 2017 GMT"), 509 }; 510 511 static const struct { 512 ASN1_TIME asn1; 513 const char *readable; 514 } x509_print_tests_iso_8601 [] = { 515 /* Generalized Time */ 516 construct_asn1_time("20170731222050Z", V_ASN1_GENERALIZEDTIME, 517 "2017-07-31 22:20:50Z"), 518 /* Generalized Time, no seconds */ 519 construct_asn1_time("201707312220Z", V_ASN1_GENERALIZEDTIME, 520 "2017-07-31 22:20:00Z"), 521 /* Generalized Time, fractional seconds (3 digits) */ 522 construct_asn1_time("20170731222050.123Z", V_ASN1_GENERALIZEDTIME, 523 "2017-07-31 22:20:50.123Z"), 524 /* Generalized Time, fractional seconds (1 digit) */ 525 construct_asn1_time("20170731222050.1Z", V_ASN1_GENERALIZEDTIME, 526 "2017-07-31 22:20:50.1Z"), 527 /* Generalized Time, fractional seconds (0 digit) */ 528 construct_asn1_time("20170731222050.Z", V_ASN1_GENERALIZEDTIME, 529 "Bad time value"), 530 /* UTC Time */ 531 construct_asn1_time("170731222050Z", V_ASN1_UTCTIME, 532 "2017-07-31 22:20:50Z"), 533 /* UTC Time, no seconds */ 534 construct_asn1_time("1707312220Z", V_ASN1_UTCTIME, 535 "2017-07-31 22:20:00Z"), 536 }; 537 538 static int test_x509_time_print_rfc_822(int idx) 539 { 540 BIO *m; 541 int ret = 0, rv; 542 char *pp; 543 const char *readable; 544 545 if (!TEST_ptr(m = BIO_new(BIO_s_mem()))) 546 goto err; 547 548 rv = ASN1_TIME_print_ex(m, &x509_print_tests_rfc_822[idx].asn1, ASN1_DTFLGS_RFC822); 549 readable = x509_print_tests_rfc_822[idx].readable; 550 551 if (rv == 0 && !TEST_str_eq(readable, "Bad time value")) { 552 /* only if the test case intends to fail... */ 553 goto err; 554 } 555 if (!TEST_int_ne(rv = BIO_get_mem_data(m, &pp), 0) 556 || !TEST_int_eq(rv, (int)strlen(readable)) 557 || !TEST_strn_eq(pp, readable, rv)) 558 goto err; 559 560 ret = 1; 561 err: 562 BIO_free(m); 563 return ret; 564 } 565 566 static int test_x509_time_print_iso_8601(int idx) 567 { 568 BIO *m; 569 int ret = 0, rv; 570 char *pp; 571 const char *readable; 572 573 if (!TEST_ptr(m = BIO_new(BIO_s_mem()))) 574 goto err; 575 576 rv = ASN1_TIME_print_ex(m, &x509_print_tests_iso_8601[idx].asn1, ASN1_DTFLGS_ISO8601); 577 readable = x509_print_tests_iso_8601[idx].readable; 578 579 if (rv == 0 && !TEST_str_eq(readable, "Bad time value")) { 580 /* only if the test case intends to fail... */ 581 goto err; 582 } 583 if (!TEST_int_ne(rv = BIO_get_mem_data(m, &pp), 0) 584 || !TEST_int_eq(rv, (int)strlen(readable)) 585 || !TEST_strn_eq(pp, readable, rv)) 586 goto err; 587 588 ret = 1; 589 err: 590 BIO_free(m); 591 return ret; 592 } 593 594 int setup_tests(void) 595 { 596 ADD_TEST(test_x509_cmp_time_current); 597 ADD_TEST(test_X509_cmp_timeframe); 598 ADD_ALL_TESTS(test_x509_cmp_time, OSSL_NELEM(x509_cmp_tests)); 599 ADD_ALL_TESTS(test_x509_time, OSSL_NELEM(x509_format_tests)); 600 ADD_ALL_TESTS(test_days, OSSL_NELEM(day_of_week_tests)); 601 ADD_ALL_TESTS(test_x509_time_print_rfc_822, OSSL_NELEM(x509_print_tests_rfc_822)); 602 ADD_ALL_TESTS(test_x509_time_print_iso_8601, OSSL_NELEM(x509_print_tests_iso_8601)); 603 return 1; 604 } 605