1 /* $OpenBSD: asn1object.c,v 1.14 2024/05/29 17:23:05 tb Exp $ */
2 /*
3  * Copyright (c) 2017, 2021, 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/asn1.h>
19 #include <openssl/err.h>
20 #include <openssl/objects.h>
21 
22 #include <err.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "asn1_local.h"
27 
28 static void
hexdump(const unsigned char * buf,int len)29 hexdump(const unsigned char *buf, int len)
30 {
31 	int i;
32 
33 	if (len <= 0) {
34 		fprintf(stderr, "<negative length %d>\n", len);
35 		return;
36 	}
37 
38 	for (i = 1; i <= len; i++)
39 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
40 
41 	fprintf(stderr, "\n");
42 }
43 
44 static int
asn1_compare_bytes(const char * label,const unsigned char * d1,int len1,const unsigned char * d2,int len2)45 asn1_compare_bytes(const char *label, const unsigned char *d1, int len1,
46     const unsigned char *d2, int len2)
47 {
48 	if (len1 != len2) {
49 		fprintf(stderr, "FAIL: %s - byte lengths differ "
50 		    "(%d != %d)\n", label, len1, len2);
51 		fprintf(stderr, "Got:\n");
52 		hexdump(d1, len1);
53 		fprintf(stderr, "Want:\n");
54 		hexdump(d2, len2);
55 		return 0;
56 	}
57 	if (memcmp(d1, d2, len1) != 0) {
58 		fprintf(stderr, "FAIL: %s - bytes differ\n", label);
59 		fprintf(stderr, "Got:\n");
60 		hexdump(d1, len1);
61 		fprintf(stderr, "Want:\n");
62 		hexdump(d2, len2);
63 		return 0;
64 	}
65 	return 1;
66 }
67 
68 struct asn1_object_test {
69 	const char *oid;
70 	const char *txt;
71 	const uint8_t content[255];
72 	size_t content_len;
73 	const uint8_t der[255];
74 	size_t der_len;
75 	int want_error;
76 };
77 
78 struct asn1_object_test asn1_object_tests[] = {
79 	{
80 		.oid = "2.5",
81 		.txt = "directory services (X.500)",
82 		.content = {
83 			0x55,
84 		},
85 		.content_len = 1,
86 		.der = {
87 			0x06, 0x01, 0x55,
88 		},
89 		.der_len = 3,
90 	},
91 	{
92 		.oid = "2.5.4",
93 		.txt = "X509",
94 		.content = {
95 			0x55, 0x04,
96 		},
97 		.content_len = 2,
98 		.der = {
99 			0x06, 0x02, 0x55, 0x04,
100 		},
101 		.der_len = 4,
102 	},
103 	{
104 		.oid = "2.5.4.10",
105 		.txt = "organizationName",
106 		.content = {
107 			0x55, 0x04, 0x0a,
108 		},
109 		.content_len = 3,
110 		.der = {
111 			0x06, 0x03, 0x55, 0x04, 0x0a,
112 		},
113 		.der_len = 5,
114 	},
115 	{
116 		.oid = "2 5 4 10",
117 		.txt = "organizationName",
118 		.content = {
119 			0x55, 0x04, 0x0a,
120 		},
121 		.content_len = 3,
122 		.der = {
123 			0x06, 0x03, 0x55, 0x04, 0x0a,
124 		},
125 		.der_len = 5,
126 	},
127 	{
128 		.oid = "2.5.0.0",
129 		.txt = "2.5.0.0",
130 		.content = {
131 			0x55, 0x00, 0x00,
132 		},
133 		.content_len = 3,
134 		.der = {
135 			0x06, 0x03, 0x55, 0x00, 0x00,
136 		},
137 		.der_len = 5,
138 	},
139 	{
140 		.oid = "0.0.0.0",
141 		.txt = "0.0.0.0",
142 		.content = {
143 			0x00, 0x00, 0x00,
144 		},
145 		.content_len = 3,
146 		.der = {
147 			0x06, 0x03, 0x00, 0x00, 0x00,
148 		},
149 		.der_len = 5,
150 	},
151 	{
152 		.oid = "1.3.6.1.4.1.11129.2.4.5",
153 		.txt = "CT Certificate SCTs",
154 		.content = {
155 			0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02,
156 			0x04, 0x05,
157 		},
158 		.content_len = 10,
159 		.der = {
160 			0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
161 			0x79, 0x02, 0x04, 0x05,
162 		},
163 		.der_len = 12,
164 	},
165 	{
166 		.oid = "2.00005.0000000000004.10",
167 		.want_error = ASN1_R_INVALID_NUMBER,
168 	},
169 	{
170 		.oid = "2..5.4.10",
171 		.want_error = ASN1_R_INVALID_NUMBER,
172 	},
173 	{
174 		.oid = "2.5..4.10",
175 		.want_error = ASN1_R_INVALID_NUMBER,
176 	},
177 	{
178 		.oid = "2.5.4..10",
179 		.want_error = ASN1_R_INVALID_NUMBER,
180 	},
181 	{
182 		.oid = "2.5.4.10.",
183 		.want_error = ASN1_R_INVALID_NUMBER,
184 	},
185 	{
186 		.oid = "3.5.4.10",
187 		.want_error = ASN1_R_FIRST_NUM_TOO_LARGE,
188 	},
189 	{
190 		.oid = "0.40.4.10",
191 		.want_error = ASN1_R_SECOND_NUMBER_TOO_LARGE,
192 	},
193 	{
194 		.oid = "1.40.4.10",
195 		.want_error = ASN1_R_SECOND_NUMBER_TOO_LARGE,
196 	},
197 	{
198 		.oid = "2",
199 		.want_error = ASN1_R_MISSING_SECOND_NUMBER,
200 	},
201 	{
202 		.oid = "2.5 4.10",
203 		.want_error = ASN1_R_INVALID_SEPARATOR,
204 	},
205 	{
206 		.oid = "2,5,4,10",
207 		.want_error = ASN1_R_INVALID_SEPARATOR,
208 	},
209 	{
210 		.oid = "2.5,4.10",
211 		.want_error = ASN1_R_INVALID_DIGIT,
212 	},
213 	{
214 		.oid = "2a.5.4.10",
215 		.want_error = ASN1_R_INVALID_SEPARATOR,
216 	},
217 	{
218 		.oid = "2.5a.4.10",
219 		.want_error = ASN1_R_INVALID_DIGIT,
220 	},
221 };
222 
223 #define N_ASN1_OBJECT_TESTS \
224     (sizeof(asn1_object_tests) / sizeof(*asn1_object_tests))
225 
226 static int
do_asn1_object_test(struct asn1_object_test * aot)227 do_asn1_object_test(struct asn1_object_test *aot)
228 {
229 	ASN1_OBJECT *aobj = NULL;
230 	uint8_t buf[1024];
231 	const uint8_t *p;
232 	uint8_t *der = NULL;
233 	uint8_t *q;
234 	int err, ret;
235 	int failed = 1;
236 
237 	ERR_clear_error();
238 
239 	ret = a2d_ASN1_OBJECT(NULL, 0, aot->oid, -1);
240 	if (ret < 0 || (size_t)ret != aot->content_len) {
241 		fprintf(stderr, "FAIL: a2d_ASN1_OBJECT('%s') = %d, want %zu\n",
242 		    aot->oid, ret, aot->content_len);
243 		goto failed;
244 	}
245 	ret = a2d_ASN1_OBJECT(buf, sizeof(buf), aot->oid, -1);
246 	if (ret < 0 || (size_t)ret != aot->content_len) {
247 		fprintf(stderr, "FAIL: a2d_ASN1_OBJECT('%s') = %d, want %zu\n",
248 		    aot->oid, ret, aot->content_len);
249 		goto failed;
250 	}
251 	if (aot->content_len == 0) {
252 		err = ERR_peek_error();
253 		if (ERR_GET_REASON(err) != aot->want_error) {
254 			fprintf(stderr, "FAIL: a2d_ASN1_OBJECT('%s') - got "
255 			    "error reason %d, want %d\n", aot->oid,
256 			    ERR_GET_REASON(err), aot->want_error);
257 			goto failed;
258 		}
259 		goto done;
260 	}
261 
262 	if (!asn1_compare_bytes("ASN1_OBJECT content", buf, ret, aot->content,
263 	    aot->content_len))
264 		goto failed;
265 
266 	p = aot->content;
267 	if ((aobj = c2i_ASN1_OBJECT(NULL, &p, aot->content_len)) == NULL) {
268 		fprintf(stderr, "FAIL: c2i_ASN1_OBJECT() failed\n");
269 		goto failed;
270 	}
271 
272 	q = buf;
273 	ret = i2d_ASN1_OBJECT(aobj, &q);
274 	if (!asn1_compare_bytes("ASN1_OBJECT DER", buf, ret, aot->der,
275 	    aot->der_len))
276 		goto failed;
277 
278 	der = NULL;
279 	ret = i2d_ASN1_OBJECT(aobj, &der);
280 	if (!asn1_compare_bytes("ASN1_OBJECT DER", der, ret, aot->der,
281 	    aot->der_len))
282 		goto failed;
283 
284 	free(der);
285 	der = NULL;
286 
287 	ASN1_OBJECT_free(aobj);
288 	aobj = NULL;
289 
290 	p = aot->der;
291 	if ((aobj = d2i_ASN1_OBJECT(NULL, &p, aot->der_len)) == NULL) {
292 		fprintf(stderr, "FAIL: d2i_ASN1_OBJECT() failed\n");
293 		goto failed;
294 	}
295 	if (p != aot->der + aot->der_len) {
296 		fprintf(stderr, "FAIL: d2i_ASN1_OBJECT() p = %p, want %p\n",
297 		    p, aot->der + aot->der_len);
298 		goto failed;
299 	}
300 
301 	if (aot->txt != NULL) {
302 		ret = i2t_ASN1_OBJECT(buf, sizeof(buf), aobj);
303 		if (ret <= 0 || (size_t)ret >= sizeof(buf)) {
304 			fprintf(stderr, "FAIL: i2t_ASN1_OBJECT() failed\n");
305 			goto failed;
306 		}
307 		if (strcmp(aot->txt, buf) != 0) {
308 			fprintf(stderr, "FAIL: i2t_ASN1_OBJECT() = '%s', "
309 			    "want '%s'\n", buf, aot->txt);
310 			goto failed;
311 		}
312 	}
313 
314  done:
315 	failed = 0;
316 
317  failed:
318 	ASN1_OBJECT_free(aobj);
319 	free(der);
320 
321 	return failed;
322 }
323 
324 static int
asn1_object_test(void)325 asn1_object_test(void)
326 {
327 	int failed = 0;
328 	size_t i;
329 
330 	for (i = 0; i < N_ASN1_OBJECT_TESTS; i++)
331 		failed |= do_asn1_object_test(&asn1_object_tests[i]);
332 
333 	return failed;
334 }
335 
336 const uint8_t asn1_object_bad_content1[] = {
337 	0x55, 0x80, 0x04, 0x0a,
338 };
339 const uint8_t asn1_object_bad_content2[] = {
340 	0x55, 0x04, 0x8a,
341 };
342 
343 static int
asn1_object_bad_content_test(void)344 asn1_object_bad_content_test(void)
345 {
346 	ASN1_OBJECT *aobj = NULL;
347 	const uint8_t *p;
348 	size_t len;
349 	int failed = 1;
350 
351 	p = asn1_object_bad_content1;
352 	len = sizeof(asn1_object_bad_content1);
353 	if ((aobj = c2i_ASN1_OBJECT(NULL, &p, len)) != NULL) {
354 		fprintf(stderr, "FAIL: c2i_ASN1_OBJECT() succeeded with bad "
355 		    "content 1\n");
356 		goto failed;
357 	}
358 
359 	p = asn1_object_bad_content2;
360 	len = sizeof(asn1_object_bad_content2);
361 	if ((aobj = c2i_ASN1_OBJECT(NULL, &p, len)) != NULL) {
362 		fprintf(stderr, "FAIL: c2i_ASN1_OBJECT() succeeded with bad "
363 		    "content 2\n");
364 		goto failed;
365 	}
366 
367 	failed = 0;
368 
369  failed:
370 	ASN1_OBJECT_free(aobj);
371 
372 	return failed;
373 }
374 
375 static int
asn1_object_txt_test(void)376 asn1_object_txt_test(void)
377 {
378 	const char *obj_txt = "organizationName";
379 	ASN1_OBJECT *aobj = NULL;
380 	uint8_t small_buf[2];
381 	const uint8_t *p;
382 	int err, len, ret;
383 	BIO *bio = NULL;
384 	char *data;
385 	long data_len;
386 	int failed = 1;
387 
388 	ERR_clear_error();
389 
390 	ret = a2d_ASN1_OBJECT(small_buf, sizeof(small_buf), "1.2.3.4", -1);
391 	if (ret != 0) {
392 		fprintf(stderr, "FAIL: a2d_ASN1_OBJECT() with small buffer "
393 		    "returned %d, want %d\n", ret, 0);
394 		goto failed;
395 	}
396 	err = ERR_peek_error();
397 	if (ERR_GET_REASON(err) != ASN1_R_BUFFER_TOO_SMALL) {
398 		fprintf(stderr, "FAIL: Got error reason %d, want %d\n",
399 		    ERR_GET_REASON(err), ASN1_R_BUFFER_TOO_SMALL);
400 		goto failed;
401 	}
402 
403 	p = &asn1_object_tests[2].der[0];
404 	len = asn1_object_tests[2].der_len;
405 	aobj = d2i_ASN1_OBJECT(NULL, &p, len);
406 	if (aobj == NULL) {
407 		fprintf(stderr, "FAIL: d2i_ASN1_OBJECT() failed\n");
408 		goto failed;
409 	}
410 	ret = i2t_ASN1_OBJECT(small_buf, sizeof(small_buf), aobj);
411 	if (ret < 0 || (unsigned long)ret != strlen(obj_txt)) {
412 		fprintf(stderr, "FAIL: i2t_ASN1_OBJECT() with small buffer "
413 		    "returned %d, want %zu\n", ret, strlen(obj_txt));
414 		goto failed;
415 	}
416 
417 	if ((bio = BIO_new(BIO_s_mem())) == NULL) {
418 		fprintf(stderr, "FAIL: BIO_new() returned NULL\n");
419 		goto failed;
420 	}
421 	ret = i2a_ASN1_OBJECT(bio, NULL);
422 	if (ret != 4) {
423 		fprintf(stderr, "FAIL: i2a_ASN1_OBJECT(_, NULL) returned %d, "
424 		    "want 4\n", ret);
425 		goto failed;
426 	}
427 	data_len = BIO_get_mem_data(bio, &data);
428 	if (ret != data_len || memcmp("NULL", data, data_len) != 0) {
429 		fprintf(stderr, "FAIL: i2a_ASN1_OBJECT(_, NULL) did not return "
430 		    "'NULL'\n");
431 		goto failed;
432 	}
433 
434 	if ((ret = BIO_reset(bio)) <= 0) {
435 		fprintf(stderr, "FAIL: BIO_reset failed: ret = %d\n", ret);
436 		goto failed;
437 	}
438 	ret = i2a_ASN1_OBJECT(bio, aobj);
439 	if (ret < 0 || (unsigned long)ret != strlen(obj_txt)) {
440 		fprintf(stderr, "FAIL: i2a_ASN1_OBJECT() returned %d, "
441 		    "want %zu\n", ret, strlen(obj_txt));
442 		goto failed;
443 	}
444 	data_len = BIO_get_mem_data(bio, &data);
445 	if (ret != data_len || memcmp(obj_txt, data, data_len) != 0) {
446 		fprintf(stderr, "FAIL: i2a_ASN1_OBJECT() did not return "
447 		    "'%s'\n", obj_txt);
448 		goto failed;
449 	}
450 
451 	failed = 0;
452 
453  failed:
454 	ASN1_OBJECT_free(aobj);
455 	BIO_free(bio);
456 
457 	return failed;
458 }
459 
460 const uint8_t asn1_large_oid_der[] = {
461 	0x06, 0x26,
462 	0x2b, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
463 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
464 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
465 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
466 	0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
467 };
468 
469 static int
asn1_object_large_oid_test(void)470 asn1_object_large_oid_test(void)
471 {
472 	ASN1_OBJECT *aobj = NULL;
473 	uint8_t buf[1024];
474 	const uint8_t *p;
475 	uint8_t *der = NULL;
476 	uint8_t *q;
477 	int ret;
478 	int failed = 1;
479 
480 	p = asn1_large_oid_der;
481 	aobj = d2i_ASN1_OBJECT(NULL, &p, sizeof(asn1_large_oid_der));
482 	if (aobj == NULL) {
483 		fprintf(stderr, "FAIL: d2i_ASN1_OBJECT() failed with "
484 		    "large oid\n");
485 		goto failed;
486 	}
487 
488 	q = buf;
489 	ret = i2d_ASN1_OBJECT(aobj, &q);
490 	if (!asn1_compare_bytes("ASN1_OBJECT DER", buf, ret, asn1_large_oid_der,
491 	    sizeof(asn1_large_oid_der)))
492 		goto failed;
493 
494 	der = NULL;
495 	ret = i2d_ASN1_OBJECT(aobj, &der);
496 	if (!asn1_compare_bytes("ASN1_OBJECT DER", der, ret, asn1_large_oid_der,
497 	    sizeof(asn1_large_oid_der)))
498 		goto failed;
499 
500 	failed = 0;
501 
502  failed:
503 	ASN1_OBJECT_free(aobj);
504 	free(der);
505 
506 	return failed;
507 }
508 
509 static int
asn1_object_i2d_errors(void)510 asn1_object_i2d_errors(void)
511 {
512 	ASN1_OBJECT *aobj = NULL;
513 	int ret;
514 	int failed = 1;
515 
516 	if ((ret = i2d_ASN1_OBJECT(NULL, NULL)) > 0) {
517 		fprintf(stderr, "FAIL: i2d_ASN1_OBJECT(NULL, NULL) returned %d, "
518 		    "want <= 0\n", ret);
519 		goto failed;
520 	}
521 
522 	if ((aobj = OBJ_nid2obj(NID_undef)) == NULL) {
523 		fprintf(stderr, "FAIL: OBJ_nid2obj() failed\n");
524 		goto failed;
525 	}
526 
527 	if (OBJ_get0_data(aobj) != NULL) {
528 		fprintf(stderr, "FAIL: undefined obj didn't have NULL data\n");
529 		goto failed;
530 	}
531 
532 	if ((ret = i2d_ASN1_OBJECT(aobj, NULL)) > 0) {
533 		fprintf(stderr, "FAIL: i2d_ASN1_OBJECT() succeeded on undefined "
534 		    "object\n");
535 		goto failed;
536 	}
537 
538 	failed = 0;
539 
540  failed:
541 	ASN1_OBJECT_free(aobj);
542 
543 	return failed;
544 }
545 
546 int
main(int argc,char ** argv)547 main(int argc, char **argv)
548 {
549 	int failed = 0;
550 
551 	failed |= asn1_object_test();
552 	failed |= asn1_object_bad_content_test();
553 	failed |= asn1_object_txt_test();
554 	failed |= asn1_object_large_oid_test();
555 	failed |= asn1_object_i2d_errors();
556 
557 	return (failed);
558 }
559