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