xref: /openbsd/regress/lib/libcrypto/asn1/asn1time.c (revision 4bdff4be)
1 /* $OpenBSD: asn1time.c,v 1.21 2023/10/05 07:59:41 tb Exp $ */
2 /*
3  * Copyright (c) 2015 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 <openssl/asn1.h>
19 
20 #include <err.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
25 
26 struct asn1_time_test {
27 	const char *str;
28 	const char *data;
29 	const unsigned char der[32];
30 	time_t time;
31 };
32 
33 static const struct asn1_time_test asn1_invtime_tests[] = {
34 	{
35 		.str = "",
36 	},
37 	{
38 		.str = "2015",
39 	},
40 	{
41 		.str = "201509",
42 	},
43 	{
44 		.str = "20150923",
45 	},
46 	{
47 		.str = "20150923032700",
48 	},
49 	{
50 		.str = "20150923032700.Z",
51 	},
52 	{
53 		.str = "20150923032700.123",
54 	},
55 	{
56 		.str = "20150923032700+1.09",
57 	},
58 	{
59 		.str = "20150923032700+1100Z",
60 	},
61 	{
62 		.str = "20150923032700-11001",
63 	},
64 	{
65 		/* UTC time cannot have fractional seconds. */
66 		.str = "150923032700.123Z",
67 	},
68 	{
69 		.str = "aaaaaaaaaaaaaaZ",
70 	},
71 	/* utc time with omitted seconds, should fail */
72 	{
73 		.str = "1609082343Z",
74 	},
75 };
76 
77 static const struct asn1_time_test asn1_invgentime_tests[] = {
78 	/* Generalized time with omitted seconds, should fail */
79 	{
80 		.str = "201612081934Z",
81 	},
82 	/* Valid UTC time, should fail as a generalized time */
83 	{
84 		.str = "160908234300Z",
85 	},
86 };
87 
88 static const struct asn1_time_test asn1_gentime_tests[] = {
89 	{
90 		.str = "20161208193400Z",
91 		.data = "20161208193400Z",
92 		.time = 1481225640,
93 		.der = {
94 			0x18, 0x0f, 0x32, 0x30, 0x31, 0x36, 0x31, 0x32,
95 			0x30, 0x38, 0x31, 0x39, 0x33, 0x34, 0x30, 0x30,
96 			0x5a,
97 		},
98 	},
99 	{
100 		.str = "19700101000000Z",
101 		.data = "19700101000000Z",
102 		.time = 0,
103 		.der = {
104 			0x18, 0x0f, 0x31, 0x39, 0x37, 0x30, 0x30, 0x31,
105 			0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
106 			0x5a,
107 		},
108 	},
109 	{
110 		.str = "20150923032700Z",
111 		.data = "20150923032700Z",
112 		.time = 1442978820,
113 		.der = {
114 			0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x39,
115 			0x32, 0x33, 0x30, 0x33, 0x32, 0x37, 0x30, 0x30,
116 			0x5a,
117 		},
118 	},
119 };
120 
121 static const struct asn1_time_test asn1_utctime_tests[] = {
122 	{
123 		.str = "700101000000Z",
124 		.data = "700101000000Z",
125 		.time = 0,
126 		.der = {
127 			0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31,
128 			0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
129 		},
130 	},
131 	{
132 		.str = "150923032700Z",
133 		.data = "150923032700Z",
134 		.time = 1442978820,
135 		.der = {
136 			0x17, 0x0d, 0x31, 0x35, 0x30, 0x39, 0x32, 0x33,
137 			0x30, 0x33, 0x32, 0x37, 0x30, 0x30, 0x5a,
138 		},
139 	},
140 	{
141 		.str = "140524144512Z",
142 		.data = "140524144512Z",
143 		.time = 1400942712,
144 		.der = {
145 			0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x32, 0x34,
146 			0x31, 0x34, 0x34, 0x35, 0x31, 0x32, 0x5a,
147 		},
148 	},
149 	{
150 		.str = "240401144512Z",
151 		.data = "240401144512Z",
152 		.time = 1711982712,
153 		.der = {
154 			0x17, 0x0d, 0x32, 0x34, 0x30, 0x34, 0x30, 0x31,
155 			0x31, 0x34, 0x34, 0x35, 0x31, 0x32, 0x5a
156 		},
157 	},
158 };
159 
160 #define N_INVTIME_TESTS \
161     (sizeof(asn1_invtime_tests) / sizeof(*asn1_invtime_tests))
162 #define N_INVGENTIME_TESTS \
163     (sizeof(asn1_invgentime_tests) / sizeof(*asn1_invgentime_tests))
164 #define N_GENTIME_TESTS \
165     (sizeof(asn1_gentime_tests) / sizeof(*asn1_gentime_tests))
166 #define N_UTCTIME_TESTS \
167     (sizeof(asn1_utctime_tests) / sizeof(*asn1_utctime_tests))
168 
169 static void
170 hexdump(const unsigned char *buf, size_t len)
171 {
172 	size_t i;
173 
174 	for (i = 1; i <= len; i++)
175 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
176 
177 	fprintf(stderr, "\n");
178 }
179 
180 static int
181 asn1_compare_bytes(int test_no, const unsigned char *d1,
182     const unsigned char *d2, int len1, int len2)
183 {
184 	if (len1 != len2) {
185 		fprintf(stderr, "FAIL: test %d - byte lengths differ "
186 		    "(%d != %d)\n", test_no, len1, len2);
187 		return (1);
188 	}
189 	if (memcmp(d1, d2, len1) != 0) {
190 		fprintf(stderr, "FAIL: test %d - bytes differ\n", test_no);
191 		fprintf(stderr, "Got:\n");
192 		hexdump(d1, len1);
193 		fprintf(stderr, "Want:\n");
194 		hexdump(d2, len2);
195 		return (1);
196 	}
197 	return (0);
198 }
199 
200 static int
201 asn1_compare_str(int test_no, const struct asn1_string_st *asn1str,
202     const char *str)
203 {
204 	int length = strlen(str);
205 
206 	if (asn1str->length != length) {
207 		fprintf(stderr, "FAIL: test %d - string lengths differ "
208 		    "(%d != %d)\n", test_no, asn1str->length, length);
209 		return (1);
210 	}
211 	if (strncmp(asn1str->data, str, length) != 0) {
212 		fprintf(stderr, "FAIL: test %d - strings differ "
213 		    "('%s' != '%s')\n", test_no, asn1str->data, str);
214 		return (1);
215 	}
216 
217 	return (0);
218 }
219 
220 static int
221 asn1_invtime_test(int test_no, const struct asn1_time_test *att, int gen)
222 {
223 	ASN1_GENERALIZEDTIME *gt = NULL;
224 	ASN1_UTCTIME *ut = NULL;
225 	ASN1_TIME *t = NULL;
226 	int failure = 1;
227 
228 	if ((gt = ASN1_GENERALIZEDTIME_new()) == NULL)
229 		goto done;
230 	if ((ut = ASN1_UTCTIME_new()) == NULL)
231 		goto done;
232 	if ((t = ASN1_TIME_new()) == NULL)
233 		goto done;
234 
235 	if (ASN1_GENERALIZEDTIME_set_string(gt, att->str) != 0) {
236 		fprintf(stderr, "FAIL: test %d - successfully set "
237 		    "GENERALIZEDTIME string '%s'\n", test_no, att->str);
238 		goto done;
239 	}
240 
241 	if (gen)  {
242 		failure = 0;
243 		goto done;
244 	}
245 
246 	if (ASN1_UTCTIME_set_string(ut, att->str) != 0) {
247 		fprintf(stderr, "FAIL: test %d - successfully set UTCTIME "
248 		    "string '%s'\n", test_no, att->str);
249 		goto done;
250 	}
251 	if (ASN1_TIME_set_string(t, att->str) != 0) {
252 		fprintf(stderr, "FAIL: test %d - successfully set TIME "
253 		    "string '%s'\n", test_no, att->str);
254 		goto done;
255 	}
256 	if (ASN1_TIME_set_string_X509(t, att->str) != 0) {
257 		fprintf(stderr, "FAIL: test %d - successfully set x509 TIME "
258 		    "string '%s'\n", test_no, att->str);
259 		goto done;
260 	}
261 
262 	failure = 0;
263 
264  done:
265 	ASN1_GENERALIZEDTIME_free(gt);
266 	ASN1_UTCTIME_free(ut);
267 	ASN1_TIME_free(t);
268 
269 	return (failure);
270 }
271 
272 static int
273 asn1_gentime_test(int test_no, const struct asn1_time_test *att)
274 {
275 	const unsigned char *der;
276 	unsigned char *p = NULL;
277 	ASN1_GENERALIZEDTIME *gt = NULL;
278 	int failure = 1;
279 	int len;
280 	struct tm tm;
281 
282 	if (ASN1_GENERALIZEDTIME_set_string(NULL, att->str) != 1) {
283 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
284 		    test_no, att->str);
285 		goto done;
286 	}
287 
288 	if ((gt = ASN1_GENERALIZEDTIME_new()) == NULL)
289 		goto done;
290 
291 	if (ASN1_GENERALIZEDTIME_set_string(gt, att->str) != 1) {
292 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
293 		    test_no, att->str);
294 		goto done;
295 	}
296 	if (asn1_compare_str(test_no, gt, att->str) != 0)
297 		goto done;
298 
299 	if (ASN1_TIME_to_tm(gt, &tm) == 0)  {
300 		fprintf(stderr, "FAIL: test %d - ASN1_time_to_tm failed '%s'\n",
301 		    test_no, att->str);
302 		goto done;
303 	}
304 
305 	if (timegm(&tm) != att->time) {
306 		/* things with crappy time_t should die in fire */
307 		int64_t a = timegm(&tm);
308 		int64_t b = att->time;
309 		fprintf(stderr, "FAIL: test %d - times don't match, expected %lld got %lld\n",
310 		    test_no, (long long)b, (long long)a);
311 		goto done;
312 	}
313 
314 	if ((len = i2d_ASN1_GENERALIZEDTIME(gt, &p)) <= 0) {
315 		fprintf(stderr, "FAIL: test %d - i2d_ASN1_GENERALIZEDTIME "
316 		    "failed\n", test_no);
317 		goto done;
318 	}
319 	der = att->der;
320 	if (asn1_compare_bytes(test_no, p, der, len, strlen(der)) != 0)
321 		goto done;
322 
323 	len = strlen(att->der);
324 	if (d2i_ASN1_GENERALIZEDTIME(&gt, &der, len) == NULL) {
325 		fprintf(stderr, "FAIL: test %d - d2i_ASN1_GENERALIZEDTIME "
326 		    "failed\n", test_no);
327 		goto done;
328 	}
329 	if (asn1_compare_str(test_no, gt, att->str) != 0)
330 		goto done;
331 
332 	ASN1_GENERALIZEDTIME_free(gt);
333 
334 	if ((gt = ASN1_GENERALIZEDTIME_set(NULL, att->time)) == NULL) {
335 		fprintf(stderr, "FAIL: test %d - failed to set time %lld\n",
336 		    test_no, (long long)att->time);
337 		goto done;
338 	}
339 	if (asn1_compare_str(test_no, gt, att->data) != 0)
340 		goto done;
341 
342 	failure = 0;
343 
344  done:
345 	ASN1_GENERALIZEDTIME_free(gt);
346 	free(p);
347 
348 	return (failure);
349 }
350 
351 static int
352 asn1_utctime_test(int test_no, const struct asn1_time_test *att)
353 {
354 	const unsigned char *der;
355 	unsigned char *p = NULL;
356 	ASN1_UTCTIME *ut = NULL;
357 	int failure = 1;
358 	int len;
359 
360 	if (ASN1_UTCTIME_set_string(NULL, att->str) != 1) {
361 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
362 		    test_no, att->str);
363 		goto done;
364 	}
365 
366 	if ((ut = ASN1_UTCTIME_new()) == NULL)
367 		goto done;
368 
369 	if (ASN1_UTCTIME_set_string(ut, att->str) != 1) {
370 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
371 		    test_no, att->str);
372 		goto done;
373 	}
374 	if (asn1_compare_str(test_no, ut, att->str) != 0)
375 		goto done;
376 
377 	if ((len = i2d_ASN1_UTCTIME(ut, &p)) <= 0) {
378 		fprintf(stderr, "FAIL: test %d - i2d_ASN1_UTCTIME failed\n",
379 		    test_no);
380 		goto done;
381 	}
382 	der = att->der;
383 	if (asn1_compare_bytes(test_no, p, der, len, strlen(der)) != 0)
384 		goto done;
385 
386 	len = strlen(att->der);
387 	if (d2i_ASN1_UTCTIME(&ut, &der, len) == NULL) {
388 		fprintf(stderr, "FAIL: test %d - d2i_ASN1_UTCTIME failed\n",
389 		    test_no);
390 		goto done;
391 	}
392 	if (asn1_compare_str(test_no, ut, att->str) != 0)
393 		goto done;
394 
395 	ASN1_UTCTIME_free(ut);
396 
397 	if ((ut = ASN1_UTCTIME_set(NULL, att->time)) == NULL) {
398 		fprintf(stderr, "FAIL: test %d - failed to set time %lld\n",
399 		    test_no, (long long)att->time);
400 		goto done;
401 	}
402 	if (asn1_compare_str(test_no, ut, att->data) != 0)
403 		goto done;
404 
405 	failure = 0;
406 
407  done:
408 	ASN1_UTCTIME_free(ut);
409 	free(p);
410 
411 	return (failure);
412 }
413 
414 static int
415 asn1_time_test(int test_no, const struct asn1_time_test *att, int type)
416 {
417 	ASN1_TIME *t = NULL, *tx509 = NULL;
418 	int failure = 1;
419 
420 	if (ASN1_TIME_set_string(NULL, att->str) != 1) {
421 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
422 		    test_no, att->str);
423 		goto done;
424 	}
425 
426 	if ((t = ASN1_TIME_new()) == NULL)
427 		goto done;
428 
429 	if ((tx509 = ASN1_TIME_new()) == NULL)
430 		goto done;
431 
432 	if (ASN1_TIME_set_string(t, att->str) != 1) {
433 		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
434 		    test_no, att->str);
435 		goto done;
436 	}
437 
438 	if (t->type != type) {
439 		fprintf(stderr, "FAIL: test %d - got type %d, want %d\n",
440 		    test_no, t->type, type);
441 		goto done;
442 	}
443 
444 	if (ASN1_TIME_normalize(t) != 1) {
445 		fprintf(stderr, "FAIL: test %d - failed to set normalize '%s'\n",
446 		    test_no, att->str);
447 		goto done;
448 	}
449 
450 	if (ASN1_TIME_set_string_X509(tx509, t->data) != 1) {
451 		fprintf(stderr, "FAIL: test %d - failed to set string X509 '%s'\n",
452 		    test_no, t->data);
453 		goto done;
454 	}
455 
456 	if (t->type != tx509->type) {
457 		fprintf(stderr, "FAIL: test %d - type %d, different from %d\n",
458 		    test_no, t->type, tx509->type);
459 		goto done;
460 	}
461 
462 	if (ASN1_TIME_compare(t, tx509) != 0) {
463 		fprintf(stderr, "FAIL: ASN1_TIME values differ!\n");
464 		goto done;
465 	}
466 
467 
468 	failure = 0;
469 
470  done:
471 
472 	ASN1_TIME_free(t);
473 	ASN1_TIME_free(tx509);
474 
475 	return (failure);
476 }
477 
478 static int
479 time_t_cmp(time_t t1, time_t t2)
480 {
481 	if (t1 < t2)
482 		return -1;
483 	if (t2 < t1)
484 		return 1;
485 	return 0;
486 }
487 
488 static int
489 asn1_time_compare_families(const struct asn1_time_test *fam1, size_t fam1_size,
490     const struct asn1_time_test *fam2, size_t fam2_size)
491 {
492 	const struct asn1_time_test *att1, *att2;
493 	ASN1_TIME *t1 = NULL, *t2 = NULL;
494 	size_t i, j;
495 	int asn1_cmp, time_cmp;
496 	int comparison_failure = 0;
497 	int failure = 1;
498 
499 	if ((t1 = ASN1_TIME_new()) == NULL)
500 		goto done;
501 	if ((t2 = ASN1_TIME_new()) == NULL)
502 		goto done;
503 
504 	for (i = 0; i < fam1_size; i++) {
505 		att1 = &fam1[i];
506 
507 		if (!ASN1_TIME_set_string(t1, att1->str))
508 			goto done;
509 		for (j = 0; j < fam2_size; j++) {
510 			att2 = &fam2[j];
511 
512 			if (!ASN1_TIME_set_string(t2, att2->str))
513 				goto done;
514 
515 			time_cmp = time_t_cmp(att1->time, att2->time);
516 			asn1_cmp = ASN1_TIME_compare(t1, t2);
517 
518 			if (time_cmp != asn1_cmp) {
519 				fprintf(stderr, "%s vs. %s: want %d, got %d\n",
520 				    att1->str, att2->str, time_cmp, asn1_cmp);
521 				comparison_failure |= 1;
522 			}
523 
524 			time_cmp = ASN1_TIME_cmp_time_t(t1, att2->time);
525 			if (time_cmp != asn1_cmp) {
526 				fprintf(stderr, "%s vs. %lld: want %d, got %d\n",
527 				    att1->str, (long long)att2->time,
528 				    asn1_cmp, time_cmp);
529 				comparison_failure |= 1;
530 			}
531 
532 			time_cmp = ASN1_UTCTIME_cmp_time_t(t1, att2->time);
533 			if (t1->type != V_ASN1_UTCTIME)
534 				asn1_cmp = -2;
535 			if (time_cmp != asn1_cmp) {
536 				fprintf(stderr, "%s vs. %lld: want %d, got %d\n",
537 				    att1->str, (long long)att2->time,
538 				    asn1_cmp, time_cmp);
539 				comparison_failure |= 1;
540 			}
541 		}
542 	}
543 
544 	failure = comparison_failure;
545 
546  done:
547 	ASN1_TIME_free(t1);
548 	ASN1_TIME_free(t2);
549 
550 	return failure;
551 }
552 
553 static int
554 asn1_time_compare_test(void)
555 {
556 	const struct asn1_time_test *gen = asn1_gentime_tests;
557 	size_t gen_size = N_GENTIME_TESTS;
558 	const struct asn1_time_test *utc = asn1_utctime_tests;
559 	size_t utc_size = N_UTCTIME_TESTS;
560 	int failed = 0;
561 
562 	failed |= asn1_time_compare_families(gen, gen_size, gen, gen_size);
563 	failed |= asn1_time_compare_families(gen, gen_size, utc, utc_size);
564 	failed |= asn1_time_compare_families(utc, utc_size, gen, gen_size);
565 	failed |= asn1_time_compare_families(utc, utc_size, utc, utc_size);
566 
567 	return failed;
568 }
569 
570 int
571 main(int argc, char **argv)
572 {
573 	const struct asn1_time_test *att;
574 	int failed = 0;
575 	size_t i;
576 
577 	fprintf(stderr, "Invalid time tests...\n");
578 	for (i = 0; i < N_INVTIME_TESTS; i++) {
579 		att = &asn1_invtime_tests[i];
580 		failed |= asn1_invtime_test(i, att, 0);
581 	}
582 
583 	fprintf(stderr, "Invalid generalized time tests...\n");
584 	for (i = 0; i < N_INVGENTIME_TESTS; i++) {
585 		att = &asn1_invgentime_tests[i];
586 		failed |= asn1_invtime_test(i, att, 1);
587 	}
588 
589 	fprintf(stderr, "GENERALIZEDTIME tests...\n");
590 	for (i = 0; i < N_GENTIME_TESTS; i++) {
591 		att = &asn1_gentime_tests[i];
592 		failed |= asn1_gentime_test(i, att);
593 	}
594 
595 	fprintf(stderr, "UTCTIME tests...\n");
596 	for (i = 0; i < N_UTCTIME_TESTS; i++) {
597 		att = &asn1_utctime_tests[i];
598 		failed |= asn1_utctime_test(i, att);
599 	}
600 
601 	fprintf(stderr, "TIME tests...\n");
602 	for (i = 0; i < N_UTCTIME_TESTS; i++) {
603 		att = &asn1_utctime_tests[i];
604 		failed |= asn1_time_test(i, att, V_ASN1_UTCTIME);
605 	}
606 	for (i = 0; i < N_GENTIME_TESTS; i++) {
607 		att = &asn1_gentime_tests[i];
608 		failed |= asn1_time_test(i, att, V_ASN1_GENERALIZEDTIME);
609 	}
610 
611 	fprintf(stderr, "ASN1_TIME_compare tests...\n");
612 	failed |= asn1_time_compare_test();
613 
614 	/* Check for a leak in ASN1_TIME_normalize(). */
615 	failed |= ASN1_TIME_normalize(NULL) != 0;
616 
617 	return (failed);
618 }
619