1 /* $OpenBSD: asn1api.c,v 1.3 2022/07/09 14:47:42 tb Exp $ */ 2 /* 3 * Copyright (c) 2021 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 const long asn1_tag2bits[] = { 26 [0] = 0, 27 [1] = 0, 28 [2] = 0, 29 [3] = B_ASN1_BIT_STRING, 30 [4] = B_ASN1_OCTET_STRING, 31 [5] = 0, 32 [6] = 0, 33 [7] = B_ASN1_UNKNOWN, 34 [8] = B_ASN1_UNKNOWN, 35 [9] = B_ASN1_UNKNOWN, 36 [10] = B_ASN1_UNKNOWN, 37 [11] = B_ASN1_UNKNOWN, 38 [12] = B_ASN1_UTF8STRING, 39 [13] = B_ASN1_UNKNOWN, 40 [14] = B_ASN1_UNKNOWN, 41 [15] = B_ASN1_UNKNOWN, 42 [16] = B_ASN1_SEQUENCE, 43 [17] = 0, 44 [18] = B_ASN1_NUMERICSTRING, 45 [19] = B_ASN1_PRINTABLESTRING, 46 [20] = B_ASN1_T61STRING, 47 [21] = B_ASN1_VIDEOTEXSTRING, 48 [22] = B_ASN1_IA5STRING, 49 [23] = B_ASN1_UTCTIME, 50 [24] = B_ASN1_GENERALIZEDTIME, 51 [25] = B_ASN1_GRAPHICSTRING, 52 [26] = B_ASN1_ISO64STRING, 53 [27] = B_ASN1_GENERALSTRING, 54 [28] = B_ASN1_UNIVERSALSTRING, 55 [29] = B_ASN1_UNKNOWN, 56 [30] = B_ASN1_BMPSTRING, 57 }; 58 59 static int 60 asn1_tag2bit(void) 61 { 62 int failed = 1; 63 long bit; 64 int i; 65 66 for (i = -3; i <= V_ASN1_NEG + 30; i++) { 67 bit = ASN1_tag2bit(i); 68 if (i >= 0 && i <= 30) { 69 if (bit != asn1_tag2bits[i]) { 70 fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx," 71 " want 0x%lx\n", i, bit, asn1_tag2bits[i]); 72 goto failed; 73 } 74 } else { 75 if (bit != 0) { 76 fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx," 77 " want 0x0\n", i, bit); 78 goto failed; 79 } 80 } 81 } 82 83 failed = 0; 84 85 failed: 86 return failed; 87 } 88 89 static int 90 asn1_tag2str(void) 91 { 92 int failed = 1; 93 const char *s; 94 int i; 95 96 for (i = -3; i <= V_ASN1_NEG + 30; i++) { 97 if ((s = ASN1_tag2str(i)) == NULL) { 98 fprintf(stderr, "FAIL: ASN1_tag2str(%d) returned " 99 "NULL\n", i); 100 goto failed; 101 } 102 if ((i >= 0 && i <= 30) || i == V_ASN1_NEG_INTEGER || 103 i == V_ASN1_NEG_ENUMERATED) { 104 if (strcmp(s, "(unknown)") == 0) { 105 fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s'," 106 " want tag name\n", i, s); 107 goto failed; 108 } 109 } else { 110 if (strcmp(s, "(unknown)") != 0) { 111 fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s'," 112 " want '(unknown')\n", i, s); 113 goto failed; 114 } 115 } 116 } 117 118 failed = 0; 119 120 failed: 121 return failed; 122 } 123 124 struct asn1_get_object_test { 125 const uint8_t asn1[64]; 126 size_t asn1_len; 127 size_t asn1_hdr_len; 128 int want_ret; 129 long want_length; 130 int want_tag; 131 int want_class; 132 int want_error; 133 }; 134 135 const struct asn1_get_object_test asn1_get_object_tests[] = { 136 { 137 /* Zero tag and zero length (EOC). */ 138 .asn1 = {0x00, 0x00}, 139 .asn1_len = 2, 140 .asn1_hdr_len = 2, 141 .want_ret = 0x00, 142 .want_length = 0, 143 .want_tag = 0, 144 .want_class = 0, 145 }, 146 { 147 /* Boolean with short form length. */ 148 .asn1 = {0x01, 0x01}, 149 .asn1_len = 3, 150 .asn1_hdr_len = 2, 151 .want_ret = 0x00, 152 .want_length = 1, 153 .want_tag = 1, 154 .want_class = 0, 155 }, 156 { 157 /* Long form tag. */ 158 .asn1 = {0x1f, 0x7f, 0x01}, 159 .asn1_len = 3 + 128, 160 .asn1_hdr_len = 3, 161 .want_ret = 0x00, 162 .want_length = 1, 163 .want_tag = 127, 164 .want_class = 0, 165 }, 166 { 167 /* Long form tag with class application. */ 168 .asn1 = {0x5f, 0x7f, 0x01}, 169 .asn1_len = 3 + 128, 170 .asn1_hdr_len = 3, 171 .want_ret = 0x00, 172 .want_length = 1, 173 .want_tag = 127, 174 .want_class = 1 << 6, 175 }, 176 { 177 /* Long form tag with class context-specific. */ 178 .asn1 = {0x9f, 0x7f, 0x01}, 179 .asn1_len = 3 + 128, 180 .asn1_hdr_len = 3, 181 .want_ret = 0x00, 182 .want_length = 1, 183 .want_tag = 127, 184 .want_class = 2 << 6, 185 }, 186 { 187 /* Long form tag with class private. */ 188 .asn1 = {0xdf, 0x7f, 0x01}, 189 .asn1_len = 3 + 128, 190 .asn1_hdr_len = 3, 191 .want_ret = 0x00, 192 .want_length = 1, 193 .want_tag = 127, 194 .want_class = 3 << 6, 195 }, 196 { 197 /* Long form tag (maximum). */ 198 .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x01}, 199 .asn1_len = 8, 200 .asn1_hdr_len = 7, 201 .want_ret = 0x00, 202 .want_length = 1, 203 .want_tag = 0x7fffffff, 204 .want_class = 0, 205 }, 206 { 207 /* Long form tag (maximum + 1). */ 208 .asn1 = {0x1f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x01}, 209 .asn1_len = 8, 210 .asn1_hdr_len = 7, 211 .want_ret = 0x80, 212 .want_error = ASN1_R_HEADER_TOO_LONG, 213 }, 214 { 215 /* OctetString with long form length. */ 216 .asn1 = {0x04, 0x81, 0x80}, 217 .asn1_len = 3 + 128, 218 .asn1_hdr_len = 3, 219 .want_ret = 0x00, 220 .want_length = 128, 221 .want_tag = 4, 222 .want_class = 0, 223 }, 224 { 225 /* OctetString with long form length. */ 226 .asn1 = {0x04, 0x84, 0x7f, 0xff, 0xff, 0xf9}, 227 .asn1_len = 0x7fffffff, 228 .asn1_hdr_len = 6, 229 .want_ret = 0x00, 230 .want_length = 0x7ffffff9, 231 .want_tag = 4, 232 .want_class = 0, 233 }, 234 { 235 /* Long form tag and long form length. */ 236 .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x84, 0x7f, 0xff, 0xff, 0xf4}, 237 .asn1_len = 0x7fffffff, 238 .asn1_hdr_len = 11, 239 .want_ret = 0x00, 240 .want_length = 0x7ffffff4, 241 .want_tag = 0x7fffffff, 242 .want_class = 0, 243 }, 244 { 245 /* Constructed OctetString with definite length. */ 246 .asn1 = {0x24, 0x03}, 247 .asn1_len = 5, 248 .asn1_hdr_len = 2, 249 .want_ret = 0x20, 250 .want_length = 3, 251 .want_tag = 4, 252 .want_class = 0, 253 }, 254 { 255 /* Constructed OctetString with indefinite length. */ 256 .asn1 = {0x24, 0x80}, 257 .asn1_len = 5, 258 .asn1_hdr_len = 2, 259 .want_ret = 0x21, 260 .want_length = 0, 261 .want_tag = 4, 262 .want_class = 0, 263 }, 264 { 265 /* Boolean with indefinite length (invalid). */ 266 .asn1 = {0x01, 0x80}, 267 .asn1_len = 3, 268 .want_ret = 0x80, 269 .want_error = ASN1_R_HEADER_TOO_LONG, 270 }, 271 { 272 /* OctetString with insufficient data (only tag). */ 273 .asn1 = {0x04, 0x04}, 274 .asn1_len = 1, 275 .want_ret = 0x80, 276 .want_error = ASN1_R_HEADER_TOO_LONG, 277 }, 278 { 279 /* OctetString with insufficient data (missing content). */ 280 .asn1 = {0x04, 0x04}, 281 .asn1_len = 2, 282 .asn1_hdr_len = 2, 283 .want_ret = 0x80, 284 .want_length = 4, 285 .want_tag = 4, 286 .want_class = 0, 287 .want_error = ASN1_R_TOO_LONG, 288 }, 289 { 290 /* OctetString with insufficient data (partial content). */ 291 .asn1 = {0x04, 0x04}, 292 .asn1_len = 5, 293 .asn1_hdr_len = 2, 294 .want_ret = 0x80, 295 .want_length = 4, 296 .want_tag = 4, 297 .want_class = 0, 298 .want_error = ASN1_R_TOO_LONG, 299 }, 300 { 301 /* Constructed OctetString with insufficient data (only tag/len). */ 302 .asn1 = {0x24, 0x04}, 303 .asn1_len = 2, 304 .asn1_hdr_len = 2, 305 .want_ret = 0xa0, 306 .want_length = 4, 307 .want_tag = 4, 308 .want_class = 0, 309 .want_error = ASN1_R_TOO_LONG, 310 }, 311 }; 312 313 #define N_ASN1_GET_OBJECT_TESTS \ 314 (sizeof(asn1_get_object_tests) / sizeof(*asn1_get_object_tests)) 315 316 static int 317 asn1_get_object(void) 318 { 319 const struct asn1_get_object_test *agot; 320 const uint8_t *p; 321 int ret, tag, tag_class; 322 long err, length; 323 size_t i; 324 int failed = 1; 325 326 for (i = 0; i < N_ASN1_GET_OBJECT_TESTS; i++) { 327 agot = &asn1_get_object_tests[i]; 328 329 ERR_clear_error(); 330 331 p = agot->asn1; 332 ret = ASN1_get_object(&p, &length, &tag, &tag_class, agot->asn1_len); 333 334 if (ret != agot->want_ret) { 335 fprintf(stderr, "FAIL: %zu - got return value %x, want %x\n", 336 i, ret, agot->want_ret); 337 goto failed; 338 } 339 if (ret & 0x80) { 340 err = ERR_peek_error(); 341 if (ERR_GET_REASON(err) != agot->want_error) { 342 fprintf(stderr, "FAIL: %zu - got error reason %d, " 343 "want %d\n", i, ERR_GET_REASON(err), 344 agot->want_error); 345 goto failed; 346 } 347 if (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG) { 348 if (p != agot->asn1) { 349 fprintf(stderr, "FAIL: %zu - got ber_in %p, " 350 "want %p\n", i, p, agot->asn1); 351 goto failed; 352 } 353 continue; 354 } 355 } 356 if (length != agot->want_length) { 357 fprintf(stderr, "FAIL: %zu - got length %ld, want %ld\n", 358 i, length, agot->want_length); 359 goto failed; 360 } 361 if (tag != agot->want_tag) { 362 fprintf(stderr, "FAIL: %zu - got tag %d, want %d\n", 363 i, tag, agot->want_tag); 364 goto failed; 365 } 366 if (tag_class != agot->want_class) { 367 fprintf(stderr, "FAIL: %zu - got class %d, want %d\n", 368 i, tag_class, agot->want_class); 369 goto failed; 370 } 371 if (p != agot->asn1 + agot->asn1_hdr_len) { 372 fprintf(stderr, "FAIL: %zu - got ber_in %p, want %p\n", 373 i, p, agot->asn1 + agot->asn1_len); 374 goto failed; 375 } 376 } 377 378 failed = 0; 379 380 failed: 381 return failed; 382 } 383 384 static int 385 asn1_integer_get_null_test(void) 386 { 387 int failed = 0; 388 long ret; 389 390 if ((ret = ASN1_INTEGER_get(NULL)) != 0) { 391 fprintf(stderr, "FAIL: ASN1_INTEGER_get(NULL) %ld != 0\n", ret); 392 failed |= 1; 393 } 394 395 if ((ret = ASN1_ENUMERATED_get(NULL)) != 0) { 396 fprintf(stderr, "FAIL: ASN1_ENUMERATED_get(NULL) %ld != 0\n", 397 ret); 398 failed |= 1; 399 } 400 401 return failed; 402 } 403 404 int 405 main(int argc, char **argv) 406 { 407 int failed = 0; 408 409 failed |= asn1_tag2bit(); 410 failed |= asn1_tag2str(); 411 failed |= asn1_get_object(); 412 failed |= asn1_integer_get_null_test(); 413 414 return (failed); 415 } 416