1/* $OpenBSD: rfc5280time.c,v 1.8 2024/04/08 19:57:40 beck Exp $ */
2/*
3 * Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
4 * Copyright (c) 2015 Bob Beck <beck@opebsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <openssl/asn1.h>
20#include <openssl/x509.h>
21
22#include <err.h>
23#include <stdio.h>
24#include <string.h>
25
26struct rfc5280_time_test {
27	const char *str;
28	const char *data;
29	time_t time;
30};
31
32struct rfc5280_time_test rfc5280_invtime_tests[] = {
33	{
34		.str = "",
35	},
36	{
37		.str = "2015",
38	},
39	{
40		.str = "201509",
41	},
42	{
43		.str = "20150923",
44	},
45	{
46		.str = "20150923032700",
47	},
48	{
49		/* UTC time must have seconds */
50		.str = "7001010000Z",
51	},
52	{
53		.str = "201509230327Z",
54	},
55	{
56		.str = "20150923032700.Z",
57	},
58	{
59		.str = "20150923032700.123",
60	},
61	{
62		.str = "20150923032700+1100Z",
63	},
64	{
65		.str = "20150923032700-11001",
66	},
67	{
68		/* UTC time cannot have fractional seconds. */
69		.str = "150923032700.123Z",
70	},
71	{
72		/* Gen time cannot have +- TZ. */
73		.str = "20150923032712+1115",
74	},
75	{
76		/* Gen time cannot have fractional seconds */
77		.str = "20150923032712.123Z",
78	},
79	{
80		.str = "aaaaaaaaaaaaaaZ",
81	},
82	{
83		/* Must be a UTC time per RFC 5280 */
84		.str = "19700101000000Z",
85		.data = "19700101000000Z",
86		.time = 0,
87	},
88	{
89		/* (times before 2050 must be UTCTIME) Per RFC 5280 4.1.2.5 */
90		.str = "20150923032700Z",
91		.data = "20150923032700Z",
92		.time = 1442978820,
93	},
94	{
95		/* (times before 2050 must be UTCTIME) Per RFC 5280 4.1.2.5 */
96		.str = "00000101000000Z",
97		.data = "00000101000000Z",
98		.time = -62167219200LL,
99	},
100	{
101		/* (times before 2050 must be UTCTIME) Per RFC 5280 4.1.2.5 */
102		.str = "20491231235959Z",
103		.data = "20491231235959Z",
104		.time = 2524607999LL,
105	},
106	{
107		/* (times before 2050 must be UTCTIME) Per RFC 5280 4.1.2.5 */
108		.str = "19500101000000Z",
109		.data = "19500101000000Z",
110		.time = -631152000LL,
111	},
112};
113
114struct rfc5280_time_test rfc5280_gentime_tests[] = {
115	{
116		/* Biggest RFC 5280 time */
117		.str = "99991231235959Z",
118		.data = "99991231235959Z",
119		.time = 253402300799LL,
120	},
121	{
122		.str = "21600218104000Z",
123		.data = "21600218104000Z",
124		.time = 6000000000LL,
125	},
126	{
127		/* Smallest RFC 5280 gen time */
128		.str = "20500101000000Z",
129		.data = "20500101000000Z",
130		.time =  2524608000LL,
131	},
132};
133struct rfc5280_time_test rfc5280_utctime_tests[] = {
134	{
135		.str = "500101000000Z",
136		.data = "500101000000Z",
137		.time = -631152000,
138	},
139	{
140		.str = "540226230640Z",
141		.data = "540226230640Z",
142		.time = -500000000,
143	},
144	{
145		.str = "491231235959Z",
146		.data = "491231235959Z",
147		.time = 2524607999LL,
148	},
149	{
150		.str = "700101000000Z",
151		.data = "700101000000Z",
152		.time = 0,
153	},
154	{
155		.str = "150923032700Z",
156		.data = "150923032700Z",
157		.time = 1442978820,
158	},
159	{
160		.str = "150923102700Z",
161		.data = "150923102700Z",
162		.time = 1443004020,
163	},
164	{
165		.str = "150922162712Z",
166		.data = "150922162712Z",
167		.time = 1442939232,
168	},
169	{
170		.str = "140524144512Z",
171		.data = "140524144512Z",
172		.time = 1400942712,
173	},
174	{
175		.str = "240401144512Z",
176		.data = "240401144512Z",
177		.time = 1711982712,
178	},
179};
180
181#define N_INVTIME_TESTS \
182    (sizeof(rfc5280_invtime_tests) / sizeof(*rfc5280_invtime_tests))
183#define N_GENTIME_TESTS \
184    (sizeof(rfc5280_gentime_tests) / sizeof(*rfc5280_gentime_tests))
185#define N_UTCTIME_TESTS \
186    (sizeof(rfc5280_utctime_tests) / sizeof(*rfc5280_utctime_tests))
187
188static int
189asn1_compare_str(int test_no, struct asn1_string_st *asn1str, const char *str)
190{
191	int length = strlen(str);
192
193	if (asn1str->length != length) {
194		fprintf(stderr, "FAIL: test %d - string lengths differ "
195		    "(%d != %d)\n", test_no, asn1str->length, length);
196		return (1);
197	}
198	if (strncmp(asn1str->data, str, length) != 0) {
199		fprintf(stderr, "FAIL: test %d - strings differ "
200		    "('%s' != '%s')\n", test_no, asn1str->data, str);
201		return (1);
202	}
203
204	return (0);
205}
206
207static int
208rfc5280_invtime_test(int test_no, struct rfc5280_time_test *att)
209{
210	ASN1_GENERALIZEDTIME *gt = NULL;
211	ASN1_UTCTIME *ut = NULL;
212	ASN1_TIME *t = NULL;
213	int failure = 1;
214	time_t now = time(NULL);
215
216	if ((gt = ASN1_GENERALIZEDTIME_new()) == NULL)
217		goto done;
218	if ((ut = ASN1_UTCTIME_new()) == NULL)
219		goto done;
220	if ((t = ASN1_TIME_new()) == NULL)
221		goto done;
222
223	if (ASN1_GENERALIZEDTIME_set_string(gt, att->str) != 0) {
224		if (X509_cmp_time(gt, &now) != 0) {
225			fprintf(stderr, "FAIL: test %d - successfully parsed as GENTIME "
226			    "string '%s'\n", test_no, att->str);
227			goto done;
228		}
229	}
230	if (ASN1_UTCTIME_set_string(ut, att->str) != 0) {
231		if (X509_cmp_time(ut, &now) != 0) {
232			fprintf(stderr, "FAIL: test %d - successfully parsed as UTCTIME "
233			    "string '%s'\n", test_no, att->str);
234			goto done;
235		}
236	}
237
238	failure = 0;
239
240 done:
241	ASN1_GENERALIZEDTIME_free(gt);
242	ASN1_UTCTIME_free(ut);
243	ASN1_TIME_free(t);
244
245	return (failure);
246}
247
248static int
249rfc5280_gentime_test(int test_no, struct rfc5280_time_test *att)
250{
251	unsigned char *p = NULL;
252	ASN1_GENERALIZEDTIME *gt;
253	int failure = 1;
254	int i;
255
256	if ((gt = ASN1_GENERALIZEDTIME_new()) == NULL)
257		goto done;
258
259	if (ASN1_GENERALIZEDTIME_set_string(gt, att->str) != 1) {
260		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
261		    test_no, att->str);
262		goto done;
263	}
264	if (asn1_compare_str(test_no, gt, att->str) != 0)
265		goto done;
266
267	if ((i = X509_cmp_time(gt, &att->time)) != -1) {
268		fprintf(stderr, "FAIL: test %d - X509_cmp_time failed - returned %d compared to %lld\n",
269		    test_no, i, (long long)att->time);
270		goto done;
271	}
272
273	att->time--;
274	if ((i = X509_cmp_time(gt, &att->time)) != 1) {
275		fprintf(stderr, "FAIL: test %d - X509_cmp_time failed - returned %d compared to %lld\n",
276		    test_no, i, (long long)att->time);
277		goto done;
278	}
279	att->time++;
280
281	ASN1_GENERALIZEDTIME_free(gt);
282
283	if ((gt = ASN1_GENERALIZEDTIME_set(NULL, att->time)) == NULL) {
284		fprintf(stderr, "FAIL: test %d - failed to set time %lld\n",
285		    test_no, (long long)att->time);
286		goto done;
287	}
288	if (asn1_compare_str(test_no, gt, att->data) != 0)
289		goto done;
290
291	failure = 0;
292
293 done:
294	ASN1_GENERALIZEDTIME_free(gt);
295	free(p);
296
297	return (failure);
298}
299
300static int
301rfc5280_utctime_test(int test_no, struct rfc5280_time_test *att)
302{
303	unsigned char *p = NULL;
304	ASN1_UTCTIME *ut;
305	int failure = 1;
306	int i;
307
308	if ((ut = ASN1_UTCTIME_new()) == NULL)
309		goto done;
310
311	if (ASN1_UTCTIME_set_string(ut, att->str) != 1) {
312		fprintf(stderr, "FAIL: test %d - failed to set string '%s'\n",
313		    test_no, att->str);
314		goto done;
315	}
316	if (asn1_compare_str(test_no, ut, att->str) != 0)
317		goto done;
318
319	if ((i = X509_cmp_time(ut, &att->time)) != -1) {
320		fprintf(stderr, "FAIL: test %d - X509_cmp_time failed - returned %d compared to %lld\n",
321		    test_no, i, (long long)att->time);
322		goto done;
323	}
324
325	att->time--;
326	if ((i = X509_cmp_time(ut, &att->time)) != 1) {
327		fprintf(stderr, "FAIL: test %d - X509_cmp_time failed - returned %d compared to %lld\n",
328		    test_no, i, (long long)att->time);
329		goto done;
330	}
331	att->time++;
332
333	ASN1_UTCTIME_free(ut);
334
335	if ((ut = ASN1_UTCTIME_set(NULL, att->time)) == NULL) {
336		fprintf(stderr, "FAIL: test %d - failed to set time %lld\n",
337		    test_no, (long long)att->time);
338		goto done;
339	}
340	if (asn1_compare_str(test_no, ut, att->data) != 0)
341		goto done;
342
343	failure = 0;
344
345 done:
346	ASN1_UTCTIME_free(ut);
347	free(p);
348
349	return (failure);
350}
351
352int
353main(int argc, char **argv)
354{
355	struct rfc5280_time_test *att;
356	int failed = 0;
357	size_t i;
358
359	fprintf(stderr, "RFC5280 Invalid time tests...\n");
360	for (i = 0; i < N_INVTIME_TESTS; i++) {
361		att = &rfc5280_invtime_tests[i];
362		failed |= rfc5280_invtime_test(i, att);
363	}
364
365	fprintf(stderr, "RFC5280 GENERALIZEDTIME tests...\n");
366	for (i = 0; i < N_GENTIME_TESTS; i++) {
367		att = &rfc5280_gentime_tests[i];
368		failed |= rfc5280_gentime_test(i, att);
369	}
370
371	fprintf(stderr, "RFC5280 UTCTIME tests...\n");
372	for (i = 0; i < N_UTCTIME_TESTS; i++) {
373		att = &rfc5280_utctime_tests[i];
374		failed |= rfc5280_utctime_test(i, att);
375	}
376	return (failed);
377}
378