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 
24 static void
hexdump(const unsigned char * buf,size_t len)25 hexdump(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 
35 static int
obj_compare_bytes(const char * label,const unsigned char * d1,int len1,const unsigned char * d2,int len2)36 obj_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 
59 struct 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 
68 struct 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 
156 static int
obj_name_test(struct obj_test * ot)157 obj_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 
204 static int
obj_name_tests(void)205 obj_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 
216 static int
obj_nid_test(struct obj_test * ot)217 obj_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 
245 static int
obj_nid_tests(void)246 obj_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 
257 static int
obj_oid_test(struct obj_test * ot)258 obj_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 
312 static int
obj_oid_tests(void)313 obj_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 
324 static int
obj_txt_test(struct obj_test * ot)325 obj_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 
399 static int
obj_txt_early_nul_test(void)400 obj_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 
441 static int
obj_txt_tests(void)442 obj_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). */
456 const 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). */
463 const 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). */
470 const 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). */
478 const 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 
487 struct oid_large_test {
488 	const char *oid;
489 	const uint8_t *asn1_der;
490 	size_t asn1_der_len;
491 	int obj2txt;
492 };
493 
494 struct 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 
524 static int
obj_oid_large_test(size_t test_no,struct oid_large_test * olt)525 obj_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 
564 static int
obj_oid_large_tests(void)565 obj_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 
576 int
main(int argc,char ** argv)577 main(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