1 /* 2 * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (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_time(int idx) 301 { 302 ASN1_TIME *t = NULL; 303 int result, rv = 0; 304 305 if (x509_format_tests[idx].set_string) { 306 /* set-string mode */ 307 t = ASN1_TIME_new(); 308 if (t == NULL) { 309 TEST_info("test_x509_time(%d) failed: internal error\n", idx); 310 return 0; 311 } 312 } 313 314 result = ASN1_TIME_set_string_X509(t, x509_format_tests[idx].data); 315 /* time string parsing result is always checked against what's expected */ 316 if (!TEST_int_eq(result, x509_format_tests[idx].expected)) { 317 TEST_info("test_x509_time(%d) failed: expected %d, got %d\n", 318 idx, x509_format_tests[idx].expected, result); 319 goto out; 320 } 321 322 /* if t is not NULL but expected_type is ignored(-1), it is an 'OK' case */ 323 if (t != NULL && x509_format_tests[idx].expected_type != -1) { 324 if (!TEST_int_eq(t->type, x509_format_tests[idx].expected_type)) { 325 TEST_info("test_x509_time(%d) failed: expected_type %d, got %d\n", 326 idx, x509_format_tests[idx].expected_type, t->type); 327 goto out; 328 } 329 } 330 331 /* if t is not NULL but expected_string is NULL, it is an 'OK' case too */ 332 if (t != NULL && x509_format_tests[idx].expected_string) { 333 if (!TEST_str_eq((const char *)t->data, 334 x509_format_tests[idx].expected_string)) { 335 TEST_info("test_x509_time(%d) failed: expected_string %s, got %s\n", 336 idx, x509_format_tests[idx].expected_string, t->data); 337 goto out; 338 } 339 } 340 341 rv = 1; 342 out: 343 if (t != NULL) 344 ASN1_TIME_free(t); 345 return rv; 346 } 347 348 static const struct { 349 int y, m, d; 350 int yd, wd; 351 } day_of_week_tests[] = { 352 /*YYYY MM DD DoY DoW */ 353 { 1900, 1, 1, 0, 1 }, 354 { 1900, 2, 28, 58, 3 }, 355 { 1900, 3, 1, 59, 4 }, 356 { 1900, 12, 31, 364, 1 }, 357 { 1901, 1, 1, 0, 2 }, 358 { 1970, 1, 1, 0, 4 }, 359 { 1999, 1, 10, 9, 0 }, 360 { 1999, 12, 31, 364, 5 }, 361 { 2000, 1, 1, 0, 6 }, 362 { 2000, 2, 28, 58, 1 }, 363 { 2000, 2, 29, 59, 2 }, 364 { 2000, 3, 1, 60, 3 }, 365 { 2000, 12, 31, 365, 0 }, 366 { 2001, 1, 1, 0, 1 }, 367 { 2008, 1, 1, 0, 2 }, 368 { 2008, 2, 28, 58, 4 }, 369 { 2008, 2, 29, 59, 5 }, 370 { 2008, 3, 1, 60, 6 }, 371 { 2008, 12, 31, 365, 3 }, 372 { 2009, 1, 1, 0, 4 }, 373 { 2011, 1, 1, 0, 6 }, 374 { 2011, 2, 28, 58, 1 }, 375 { 2011, 3, 1, 59, 2 }, 376 { 2011, 12, 31, 364, 6 }, 377 { 2012, 1, 1, 0, 0 }, 378 { 2019, 1, 2, 1, 3 }, 379 { 2019, 2, 2, 32, 6 }, 380 { 2019, 3, 2, 60, 6 }, 381 { 2019, 4, 2, 91, 2 }, 382 { 2019, 5, 2, 121, 4 }, 383 { 2019, 6, 2, 152, 0 }, 384 { 2019, 7, 2, 182, 2 }, 385 { 2019, 8, 2, 213, 5 }, 386 { 2019, 9, 2, 244, 1 }, 387 { 2019, 10, 2, 274, 3 }, 388 { 2019, 11, 2, 305, 6 }, 389 { 2019, 12, 2, 335, 1 }, 390 { 2020, 1, 2, 1, 4 }, 391 { 2020, 2, 2, 32, 0 }, 392 { 2020, 3, 2, 61, 1 }, 393 { 2020, 4, 2, 92, 4 }, 394 { 2020, 5, 2, 122, 6 }, 395 { 2020, 6, 2, 153, 2 }, 396 { 2020, 7, 2, 183, 4 }, 397 { 2020, 8, 2, 214, 0 }, 398 { 2020, 9, 2, 245, 3 }, 399 { 2020, 10, 2, 275, 5 }, 400 { 2020, 11, 2, 306, 1 }, 401 { 2020, 12, 2, 336, 3 } 402 }; 403 404 static int test_days(int n) 405 { 406 char d[16]; 407 ASN1_TIME *a = NULL; 408 struct tm t; 409 int r; 410 411 BIO_snprintf(d, sizeof(d), "%04d%02d%02d050505Z", 412 day_of_week_tests[n].y, day_of_week_tests[n].m, 413 day_of_week_tests[n].d); 414 415 if (!TEST_ptr(a = ASN1_TIME_new())) 416 return 0; 417 418 r = TEST_true(ASN1_TIME_set_string(a, d)) 419 && TEST_true(ASN1_TIME_to_tm(a, &t)) 420 && TEST_int_eq(t.tm_yday, day_of_week_tests[n].yd) 421 && TEST_int_eq(t.tm_wday, day_of_week_tests[n].wd); 422 423 ASN1_TIME_free(a); 424 return r; 425 } 426 427 #define construct_asn1_time(s, t, e) \ 428 { { sizeof(s) - 1, t, (unsigned char*)s, 0 }, e } 429 430 static const struct { 431 ASN1_TIME asn1; 432 const char *readable; 433 } x509_print_tests [] = { 434 /* Generalized Time */ 435 construct_asn1_time("20170731222050Z", V_ASN1_GENERALIZEDTIME, 436 "Jul 31 22:20:50 2017 GMT"), 437 /* Generalized Time, no seconds */ 438 construct_asn1_time("201707312220Z", V_ASN1_GENERALIZEDTIME, 439 "Jul 31 22:20:00 2017 GMT"), 440 /* Generalized Time, fractional seconds (3 digits) */ 441 construct_asn1_time("20170731222050.123Z", V_ASN1_GENERALIZEDTIME, 442 "Jul 31 22:20:50.123 2017 GMT"), 443 /* Generalized Time, fractional seconds (1 digit) */ 444 construct_asn1_time("20170731222050.1Z", V_ASN1_GENERALIZEDTIME, 445 "Jul 31 22:20:50.1 2017 GMT"), 446 /* Generalized Time, fractional seconds (0 digit) */ 447 construct_asn1_time("20170731222050.Z", V_ASN1_GENERALIZEDTIME, 448 "Bad time value"), 449 /* UTC Time */ 450 construct_asn1_time("170731222050Z", V_ASN1_UTCTIME, 451 "Jul 31 22:20:50 2017 GMT"), 452 /* UTC Time, no seconds */ 453 construct_asn1_time("1707312220Z", V_ASN1_UTCTIME, 454 "Jul 31 22:20:00 2017 GMT"), 455 }; 456 457 static int test_x509_time_print(int idx) 458 { 459 BIO *m; 460 int ret = 0, rv; 461 char *pp; 462 const char *readable; 463 464 if (!TEST_ptr(m = BIO_new(BIO_s_mem()))) 465 goto err; 466 467 rv = ASN1_TIME_print(m, &x509_print_tests[idx].asn1); 468 readable = x509_print_tests[idx].readable; 469 470 if (rv == 0 && !TEST_str_eq(readable, "Bad time value")) { 471 /* only if the test case intends to fail... */ 472 goto err; 473 } 474 if (!TEST_int_ne(rv = BIO_get_mem_data(m, &pp), 0) 475 || !TEST_int_eq(rv, (int)strlen(readable)) 476 || !TEST_strn_eq(pp, readable, rv)) 477 goto err; 478 479 ret = 1; 480 err: 481 BIO_free(m); 482 return ret; 483 } 484 485 int setup_tests(void) 486 { 487 ADD_TEST(test_x509_cmp_time_current); 488 ADD_ALL_TESTS(test_x509_cmp_time, OSSL_NELEM(x509_cmp_tests)); 489 ADD_ALL_TESTS(test_x509_time, OSSL_NELEM(x509_format_tests)); 490 ADD_ALL_TESTS(test_days, OSSL_NELEM(day_of_week_tests)); 491 ADD_ALL_TESTS(test_x509_time_print, OSSL_NELEM(x509_print_tests)); 492 return 1; 493 } 494