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