1/* $OpenBSD: objectstest.c,v 1.8 2023/05/23 11:06:52 tb Exp $ */
2/*
3 * Copyright (c) 2017, 2022 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/objects.h>
19
20#include <err.h>
21#include <stdio.h>
22#include <string.h>
23
24static void
25hexdump(const unsigned char *buf, size_t len)
26{
27	size_t i;
28
29	for (i = 1; i <= len; i++)
30		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
31
32	fprintf(stderr, "\n");
33}
34
35static int
36obj_compare_bytes(const char *label, const unsigned char *d1, int len1,
37    const unsigned char *d2, int len2)
38{
39	if (len1 != len2) {
40		fprintf(stderr, "FAIL: %s - byte lengths differ "
41		    "(%d != %d)\n", label, len1, len2);
42		fprintf(stderr, "Got:\n");
43		hexdump(d1, len1);
44		fprintf(stderr, "Want:\n");
45		hexdump(d2, len2);
46		return 0;
47	}
48	if (memcmp(d1, d2, len1) != 0) {
49		fprintf(stderr, "FAIL: %s - bytes differ\n", label);
50		fprintf(stderr, "Got:\n");
51		hexdump(d1, len1);
52		fprintf(stderr, "Want:\n");
53		hexdump(d2, len2);
54		return 0;
55	}
56	return 1;
57}
58
59struct obj_test {
60	const char *oid;
61	const char *sn;
62	const char *ln;
63	int nid;
64	uint8_t data[255];
65	size_t data_len;
66};
67
68struct obj_test obj_tests[] = {
69	{
70		.oid = NULL,
71		.sn = "UNDEF",
72		.ln = "undefined",
73		.nid = NID_undef,
74	},
75	{
76		.oid = "2.5.4.10",
77		.sn = "O",
78		.ln = "organizationName",
79		.nid = NID_organizationName,
80		.data = {
81			0x55, 0x04, 0x0a,
82		},
83		.data_len = 3,
84	},
85	{
86		.oid = "2.5.4.8",
87		.sn = "ST",
88		.ln = "stateOrProvinceName",
89		.nid = NID_stateOrProvinceName,
90		.data = {
91			0x55, 0x04, 0x08,
92		},
93		.data_len = 3,
94	},
95	{
96		.oid = "2.23.43.1",
97		.sn = "wap-wsg",
98		.nid = NID_wap_wsg,
99		.data = {
100			0x67, 0x2b, 0x01,
101		},
102		.data_len = 3,
103	},
104	{
105		.oid = "1.3.6.1.4.1.11129.2.4.5",
106		.sn = "ct_cert_scts",
107		.ln = "CT Certificate SCTs",
108		.nid = NID_ct_cert_scts,
109		.data = {
110			0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02,
111			0x04, 0x05,
112		},
113		.data_len = 10,
114	},
115	{
116		.oid = "1.3.6.1.4.1",
117		.sn = "enterprises",
118		.ln = "Enterprises",
119		.nid = NID_Enterprises,
120		.data = {
121			0x2b, 0x06, 0x01, 0x04, 0x01,
122		},
123		.data_len = 5,
124	},
125	{
126		.oid = "1.3.6.1.4.1.5454.1.70.6.11.2",
127		.nid = NID_undef,
128		.data = {
129			0x2b, 0x06, 0x01, 0x04, 0x01, 0xaa, 0x4e, 0x01,
130			0x46, 0x06, 0x0b, 0x02,
131		},
132		.data_len = 12,
133	},
134	{
135		.oid = "1.3.6.1.4.1.890.1.5.8.60.102.2",
136		.nid = NID_undef,
137		.data = {
138			0x2b, 0x06, 0x01, 0x04, 0x01, 0x86, 0x7a, 0x01,
139			0x05, 0x08, 0x3c, 0x66, 0x02,
140		},
141		.data_len = 13,
142	},
143	{
144		.oid = "1.3.6.1.4.1.173.7.3.4.1.1.26",
145		.nid = NID_undef,
146		.data = {
147			0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0x2d, 0x07,
148			0x03, 0x04, 0x01, 0x01, 0x1a,
149		},
150		.data_len = 13,
151	},
152};
153
154#define N_OBJ_TESTS (sizeof(obj_tests) / sizeof(*obj_tests))
155
156static int
157obj_name_test(struct obj_test *ot)
158{
159	const char *ln, *sn;
160	int nid;
161	int failed = 1;
162
163	if (ot->ln != NULL) {
164		if ((nid = OBJ_ln2nid(ot->ln)) != ot->nid) {
165			fprintf(stderr, "FAIL: OBJ_ln2nid() for '%s' = %d, "
166			    "want %d\n", ot->ln, nid, ot->nid);
167			goto failed;
168		}
169		if ((ln = OBJ_nid2ln(ot->nid)) == NULL) {
170			fprintf(stderr, "FAIL: OBJ_nid2ln() for '%s' returned "
171			    "NULL\n", ot->oid);
172			goto failed;
173		}
174		if (strcmp(ln, ot->ln) != 0) {
175			fprintf(stderr, "FAIL: OBJ_nid2ln() for '%s' = '%s', "
176			    "want '%s'\n", ot->oid, ln, ot->ln);
177			goto failed;
178		}
179	}
180	if (ot->sn != NULL) {
181		if ((nid = OBJ_sn2nid(ot->sn)) != ot->nid) {
182			fprintf(stderr, "FAIL: OBJ_sn2nid() for '%s' = %d, "
183			    "want %d\n", ot->sn, nid, ot->nid);
184			goto failed;
185		}
186		if ((sn = OBJ_nid2sn(ot->nid)) == NULL) {
187			fprintf(stderr, "FAIL: OBJ_nid2sn() for '%s' returned "
188			    "NULL\n", ot->oid);
189			goto failed;
190		}
191		if (strcmp(sn, ot->sn) != 0) {
192			fprintf(stderr, "FAIL: OBJ_nid2sn() for '%s' = '%s', "
193			    "want '%s'\n", ot->oid, sn, ot->sn);
194			goto failed;
195		}
196	}
197
198	failed = 0;
199
200 failed:
201	return failed;
202}
203
204static int
205obj_name_tests(void)
206{
207	int failed = 0;
208	size_t i;
209
210	for (i = 0; i < N_OBJ_TESTS; i++)
211		failed |= obj_name_test(&obj_tests[i]);
212
213	return failed;
214}
215
216static int
217obj_nid_test(struct obj_test *ot)
218{
219	ASN1_OBJECT *obj = NULL;
220	int nid;
221	int failed = 1;
222
223	if (ot->nid == NID_undef && ot->oid != NULL)
224		return 0;
225
226	if ((obj = OBJ_nid2obj(ot->nid)) == NULL) {
227		fprintf(stderr, "FAIL: OBJ_nid2obj() failed for '%s' (NID %d)\n",
228		    ot->oid, ot->nid);
229		goto failed;
230	}
231	if ((nid = OBJ_obj2nid(obj)) != ot->nid) {
232		fprintf(stderr, "FAIL: OBJ_obj2nid() failed for '%s' - got %d, "
233		    "want %d\n", ot->oid ? ot->oid : "undef", nid, ot->nid);
234		goto failed;
235	}
236
237	failed = 0;
238
239 failed:
240	ASN1_OBJECT_free(obj);
241
242	return failed;
243}
244
245static int
246obj_nid_tests(void)
247{
248	int failed = 0;
249	size_t i;
250
251	for (i = 0; i < N_OBJ_TESTS; i++)
252		failed |= obj_nid_test(&obj_tests[i]);
253
254	return failed;
255}
256
257static int
258obj_oid_test(struct obj_test *ot)
259{
260	ASN1_OBJECT *obj = NULL;
261	char buf[1024];
262	int len, nid;
263	int failed = 1;
264
265	if (ot->oid == NULL)
266		return 0;
267
268	if ((obj = OBJ_txt2obj(ot->oid, 0)) == NULL) {
269		fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s'\n", ot->oid);
270		goto failed;
271	}
272	if ((nid = OBJ_txt2nid(ot->oid)) != ot->nid) {
273		fprintf(stderr, "FAIL: OBJ_txt2nid() failed for '%s', got %d "
274		    "want %d\n", ot->oid, nid, ot->nid);
275		goto failed;
276	}
277
278	if (!obj_compare_bytes("object data", OBJ_get0_data(obj), OBJ_length(obj),
279	    ot->data, ot->data_len))
280		goto failed;
281
282	len = OBJ_obj2txt(buf, sizeof(buf), obj, 1);
283	if (len <= 0 || (size_t)len >= sizeof(buf)) {
284		fprintf(stderr, "FAIL: OBJ_obj2txt() failed for '%s'\n", ot->oid);
285		goto failed;
286	}
287	if (strcmp(buf, ot->oid) != 0) {
288		fprintf(stderr, "FAIL: OBJ_obj2txt() returned '%s', want '%s'\n",
289		    buf, ot->oid);
290		goto failed;
291	}
292
293	if ((OBJ_obj2txt(NULL, 0, obj, 1) != len)) {
294		fprintf(stderr, "FAIL: OBJ_obj2txt() with NULL buffer != %d\n",
295		    len);
296		goto failed;
297	}
298	if ((OBJ_obj2txt(buf, 3, obj, 1) != len)) {
299		fprintf(stderr, "FAIL: OBJ_obj2txt() with short buffer != %d\n",
300		    len);
301		goto failed;
302	}
303
304	failed = 0;
305
306 failed:
307	ASN1_OBJECT_free(obj);
308
309	return failed;
310}
311
312static int
313obj_oid_tests(void)
314{
315	int failed = 0;
316	size_t i;
317
318	for (i = 0; i < N_OBJ_TESTS; i++)
319		failed |= obj_oid_test(&obj_tests[i]);
320
321	return failed;
322}
323
324static int
325obj_txt_test(struct obj_test *ot)
326{
327	ASN1_OBJECT *obj = NULL;
328	const char *want;
329	char buf[1024];
330	int len, nid;
331	int failed = 1;
332
333	if (ot->oid == NULL)
334		return 0;
335
336	if (ot->sn != NULL) {
337		if ((obj = OBJ_txt2obj(ot->sn, 0)) == NULL) {
338			fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s'\n",
339			    ot->sn);
340			goto failed;
341		}
342		if ((nid = OBJ_obj2nid(obj)) != ot->nid) {
343			fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s', "
344			    "got nid %d want %d\n", ot->sn, nid, ot->nid);
345			goto failed;
346		}
347		ASN1_OBJECT_free(obj);
348		obj = NULL;
349	}
350	if (ot->ln != NULL) {
351		if ((obj = OBJ_txt2obj(ot->ln, 0)) == NULL) {
352			fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s'\n",
353			    ot->ln);
354			goto failed;
355		}
356		if ((nid = OBJ_obj2nid(obj)) != ot->nid) {
357			fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s', "
358			    "got nid %d want %d\n", ot->ln, nid, ot->nid);
359			goto failed;
360		}
361		ASN1_OBJECT_free(obj);
362		obj = NULL;
363	}
364
365	if ((obj = OBJ_txt2obj(ot->oid, 0)) == NULL) {
366		fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s'\n", ot->oid);
367		goto failed;
368	}
369	if ((nid = OBJ_obj2nid(obj)) != ot->nid) {
370		fprintf(stderr, "FAIL: OBJ_txt2obj() failed for '%s', "
371		    "got nid %d want %d\n", ot->oid, nid, ot->nid);
372		goto failed;
373	}
374
375	len = OBJ_obj2txt(buf, sizeof(buf), obj, 0);
376	if (len <= 0 || (size_t)len >= sizeof(buf)) {
377		fprintf(stderr, "FAIL: OBJ_obj2txt() failed for '%s'\n", ot->oid);
378		goto failed;
379	}
380	want = ot->ln;
381	if (want == NULL)
382		want = ot->sn;
383	if (want == NULL)
384		want = ot->oid;
385	if (strcmp(buf, want) != 0) {
386		fprintf(stderr, "FAIL: OBJ_obj2txt() returned '%s', want '%s'\n",
387		    buf, want);
388		goto failed;
389	}
390
391	failed = 0;
392
393 failed:
394	ASN1_OBJECT_free(obj);
395
396	return failed;
397}
398
399static int
400obj_txt_early_nul_test(void)
401{
402	ASN1_OBJECT *obj = NULL;
403	char buf[2];
404	int failed = 1;
405
406	buf[0] = 'x';
407	buf[1] = '\0';
408
409	if (OBJ_obj2txt(buf, sizeof(buf), NULL, 1) != 0) {
410		fprintf(stderr, "FAIL: OBJ_obj2txt(NULL) succeded\n");
411		goto failed;
412	}
413	if (buf[0] != '\0') {
414		fprintf(stderr, "FAIL: OBJ_obj2txt(NULL) did not NUL terminate\n");
415		goto failed;
416	}
417
418	if ((obj = ASN1_OBJECT_new()) == NULL)
419		errx(1, "ASN1_OBJECT_new");
420
421	buf[0] = 'x';
422	buf[1] = '\0';
423
424	if (OBJ_obj2txt(buf, sizeof(buf), obj, 1) != 0) {
425		fprintf(stderr, "FAIL: OBJ_obj2txt(obj) succeeded\n");
426		goto failed;
427	}
428	if (buf[0] != '\0') {
429		fprintf(stderr, "FAIL: OBJ_obj2txt(obj) did not NUL terminate\n");
430		goto failed;
431	}
432
433	failed = 0;
434
435 failed:
436	ASN1_OBJECT_free(obj);
437
438	return failed;
439}
440
441static int
442obj_txt_tests(void)
443{
444	int failed = 0;
445	size_t i;
446
447	for (i = 0; i < N_OBJ_TESTS; i++)
448		failed |= obj_txt_test(&obj_tests[i]);
449
450	failed |= obj_txt_early_nul_test();
451
452	return failed;
453}
454
455/* OID 1.3.18446744073709551615 (64 bits). */
456const uint8_t asn1_large_oid1[] = {
457	0x06, 0x0b,
458	0x2b, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
459	0xff, 0xff, 0x7f,
460};
461
462/* OID 1.3.18446744073709551616 (65 bits). */
463const uint8_t asn1_large_oid2[] = {
464	0x06, 0x0b,
465	0x2b, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
466	0x80, 0x80, 0x00,
467};
468
469/* OID 1.3.340282366920938463463374607431768211455 (128 bits). */
470const uint8_t asn1_large_oid3[] = {
471	0x06, 0x14,
472	0x2b, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
473	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
474	0xff, 0xff, 0xff, 0x7f,
475};
476
477/* OID 1.3.115792089237316195423570985008687907853269984665640564039457584007913129639935 (256 bits). */
478const uint8_t asn1_large_oid4[] = {
479	0x06, 0x26,
480	0x2b, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
481	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
482	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
483	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
484	0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
485};
486
487struct oid_large_test {
488	const char *oid;
489	const uint8_t *asn1_der;
490	size_t asn1_der_len;
491	int obj2txt;
492};
493
494struct oid_large_test oid_large_tests[] = {
495	{
496		.oid = "1.3.18446744073709551615",
497		.asn1_der = asn1_large_oid1,
498		.asn1_der_len = sizeof(asn1_large_oid1),
499		.obj2txt = 1,
500	},
501	{
502		.oid = "1.3.18446744073709551616",
503		.asn1_der = asn1_large_oid2,
504		.asn1_der_len = sizeof(asn1_large_oid2),
505		.obj2txt = 0,
506	},
507	{
508		.oid = "1.3.340282366920938463463374607431768211455",
509		.asn1_der = asn1_large_oid3,
510		.asn1_der_len = sizeof(asn1_large_oid3),
511		.obj2txt = 0,
512	},
513	{
514		.oid = "1.3.115792089237316195423570985008687907853269984665640"
515		    "564039457584007913129639935",
516		.asn1_der = asn1_large_oid4,
517		.asn1_der_len = sizeof(asn1_large_oid4),
518		.obj2txt = 0,
519	},
520};
521
522#define N_OID_LARGE_TESTS (sizeof(oid_large_tests) / sizeof(*oid_large_tests))
523
524static int
525obj_oid_large_test(size_t test_no, struct oid_large_test *olt)
526{
527	ASN1_OBJECT *obj = NULL;
528	const uint8_t *p;
529	char buf[1024];
530	int len;
531	int failed = 1;
532
533	p = olt->asn1_der;
534	if ((obj = d2i_ASN1_OBJECT(NULL, &p, olt->asn1_der_len)) == NULL) {
535		fprintf(stderr, "FAIL: d2i_ASN1_OBJECT() failed for large "
536		    "oid %zu\n", test_no);
537		goto failed;
538	}
539	len = OBJ_obj2txt(buf, sizeof(buf), obj, 1);
540	if (len < 0 || (size_t)len >= sizeof(buf)) {
541		fprintf(stderr, "FAIL: OBJ_obj2txt() failed for large "
542		    "oid %zu\n", test_no);
543		goto failed;
544	}
545	if ((len != 0) != olt->obj2txt) {
546		fprintf(stderr, "FAIL: OBJ_obj2txt() failed for large "
547		    "oid %zu\n", test_no);
548		goto failed;
549	}
550	if (len != 0 && strcmp(buf, olt->oid) != 0) {
551		fprintf(stderr, "FAIL: OBJ_obj2txt() returned '%s', want '%s'\n",
552		    buf, olt->oid);
553		goto failed;
554	}
555
556	failed = 0;
557
558 failed:
559	ASN1_OBJECT_free(obj);
560
561	return failed;
562}
563
564static int
565obj_oid_large_tests(void)
566{
567	int failed = 0;
568	size_t i;
569
570	for (i = 0; i < N_OID_LARGE_TESTS; i++)
571		failed |= obj_oid_large_test(i, &oid_large_tests[i]);
572
573	return failed;
574}
575
576int
577main(int argc, char **argv)
578{
579	int failed = 0;
580
581	failed |= obj_name_tests();
582	failed |= obj_nid_tests();
583	failed |= obj_oid_tests();
584	failed |= obj_txt_tests();
585	failed |= obj_oid_large_tests();
586
587	return (failed);
588}
589