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 28static void 29hexdump(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 44static int 45asn1_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 68struct 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 78struct 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 226static int 227do_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 324static int 325asn1_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 336const uint8_t asn1_object_bad_content1[] = { 337 0x55, 0x80, 0x04, 0x0a, 338}; 339const uint8_t asn1_object_bad_content2[] = { 340 0x55, 0x04, 0x8a, 341}; 342 343static int 344asn1_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 375static int 376asn1_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 460const 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 469static int 470asn1_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 509static int 510asn1_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 546int 547main(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