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