derprint.c (13791B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 #include "secutil.h" 5 #include "secoid.h" 6 7 #include <stdint.h> 8 9 #ifdef __sun 10 extern int fprintf(FILE *strm, const char *format, ... /* args */); 11 extern int fflush(FILE *stream); 12 #endif 13 14 #define RIGHT_MARGIN 24 15 /*#define RAW_BYTES 1 */ 16 17 static int prettyColumn = 0; 18 19 static int 20 getInteger256(const unsigned char *data, unsigned int nb) 21 { 22 int val; 23 24 switch (nb) { 25 case 1: 26 val = data[0]; 27 break; 28 case 2: 29 val = (data[0] << 8) | data[1]; 30 break; 31 case 3: 32 val = (data[0] << 16) | (data[1] << 8) | data[2]; 33 break; 34 case 4: 35 /* If the most significant bit of data[0] is 1, val would be negative. 36 * Treat it as an error. 37 */ 38 if (data[0] & 0x80) { 39 PORT_SetError(SEC_ERROR_BAD_DER); 40 return -1; 41 } 42 val = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; 43 break; 44 default: 45 PORT_SetError(SEC_ERROR_BAD_DER); 46 return -1; 47 } 48 49 return val; 50 } 51 52 static int 53 prettyNewline(FILE *out) 54 { 55 int rv; 56 57 if (prettyColumn != -1) { 58 rv = fprintf(out, "\n"); 59 prettyColumn = -1; 60 if (rv < 0) { 61 PORT_SetError(SEC_ERROR_IO); 62 return rv; 63 } 64 } 65 return 0; 66 } 67 68 static int 69 prettyIndent(FILE *out, unsigned level) 70 { 71 unsigned int i; 72 int rv; 73 74 if (prettyColumn == -1) { 75 prettyColumn = level; 76 for (i = 0; i < level; i++) { 77 rv = fprintf(out, " "); 78 if (rv < 0) { 79 PORT_SetError(SEC_ERROR_IO); 80 return rv; 81 } 82 } 83 } 84 85 return 0; 86 } 87 88 static int 89 prettyPrintByte(FILE *out, unsigned char item, unsigned int level) 90 { 91 int rv; 92 93 rv = prettyIndent(out, level); 94 if (rv < 0) 95 return rv; 96 97 rv = fprintf(out, "%02x ", item); 98 if (rv < 0) { 99 PORT_SetError(SEC_ERROR_IO); 100 return rv; 101 } 102 103 prettyColumn++; 104 if (prettyColumn >= RIGHT_MARGIN) { 105 return prettyNewline(out); 106 } 107 108 return 0; 109 } 110 111 static int 112 prettyPrintLeaf(FILE *out, const unsigned char *data, 113 unsigned int len, unsigned int lv) 114 { 115 unsigned int i; 116 int rv; 117 118 for (i = 0; i < len; i++) { 119 rv = prettyPrintByte(out, *data++, lv); 120 if (rv < 0) 121 return rv; 122 } 123 return prettyNewline(out); 124 } 125 126 static int 127 prettyPrintStringStart(FILE *out, const unsigned char *str, 128 unsigned int len, unsigned int level) 129 { 130 #define BUF_SIZE 100 131 unsigned char buf[BUF_SIZE]; 132 int rv; 133 134 if (len >= BUF_SIZE) 135 len = BUF_SIZE - 1; 136 137 rv = prettyNewline(out); 138 if (rv < 0) 139 return rv; 140 141 rv = prettyIndent(out, level); 142 if (rv < 0) 143 return rv; 144 145 memcpy(buf, str, len); 146 buf[len] = '\000'; 147 148 rv = fprintf(out, "\"%s\"", buf); 149 if (rv < 0) { 150 PORT_SetError(SEC_ERROR_IO); 151 return rv; 152 } 153 154 return 0; 155 #undef BUF_SIZE 156 } 157 158 static int 159 prettyPrintString(FILE *out, const unsigned char *str, 160 unsigned int len, unsigned int level, PRBool raw) 161 { 162 int rv; 163 164 rv = prettyPrintStringStart(out, str, len, level); 165 if (rv < 0) 166 return rv; 167 168 rv = prettyNewline(out); 169 if (rv < 0) 170 return rv; 171 172 if (raw) { 173 rv = prettyPrintLeaf(out, str, len, level); 174 if (rv < 0) 175 return rv; 176 } 177 178 return 0; 179 } 180 181 static int 182 prettyPrintTime(FILE *out, const unsigned char *str, 183 unsigned int len, unsigned int level, PRBool raw, PRBool utc) 184 { 185 SECItem time_item; 186 int rv; 187 188 rv = prettyPrintStringStart(out, str, len, level); 189 if (rv < 0) 190 return rv; 191 192 time_item.data = (unsigned char *)str; 193 time_item.len = len; 194 195 rv = fprintf(out, " ("); 196 if (rv < 0) { 197 PORT_SetError(SEC_ERROR_IO); 198 return rv; 199 } 200 201 if (utc) 202 SECU_PrintUTCTime(out, &time_item, NULL, 0); 203 else 204 SECU_PrintGeneralizedTime(out, &time_item, NULL, 0); 205 206 rv = fprintf(out, ")"); 207 if (rv < 0) { 208 PORT_SetError(SEC_ERROR_IO); 209 return rv; 210 } 211 212 rv = prettyNewline(out); 213 if (rv < 0) 214 return rv; 215 216 if (raw) { 217 rv = prettyPrintLeaf(out, str, len, level); 218 if (rv < 0) 219 return rv; 220 } 221 222 return 0; 223 } 224 225 static int 226 prettyPrintObjectID(FILE *out, const unsigned char *data, 227 unsigned int len, unsigned int level, PRBool raw) 228 { 229 SECOidData *oiddata; 230 SECItem oiditem; 231 unsigned int i; 232 unsigned long val; 233 int rv; 234 235 /* 236 * First print the Object Id in numeric format 237 */ 238 239 rv = prettyIndent(out, level); 240 if (rv < 0) 241 return rv; 242 243 if (len == 0) { 244 PORT_SetError(SEC_ERROR_BAD_DER); 245 return -1; 246 } 247 val = data[0]; 248 i = val % 40; 249 val = val / 40; 250 rv = fprintf(out, "%lu %u ", val, i); 251 if (rv < 0) { 252 PORT_SetError(SEC_ERROR_IO); 253 return rv; 254 } 255 256 val = 0; 257 for (i = 1; i < len; ++i) { 258 unsigned long j; 259 260 j = data[i]; 261 val = (val << 7) | (j & 0x7f); 262 if (j & 0x80) 263 continue; 264 rv = fprintf(out, "%lu ", val); 265 if (rv < 0) { 266 PORT_SetError(SEC_ERROR_IO); 267 return rv; 268 } 269 val = 0; 270 } 271 272 /* 273 * Now try to look it up and print a symbolic version. 274 */ 275 oiditem.data = (unsigned char *)data; 276 oiditem.len = len; 277 oiddata = SECOID_FindOID(&oiditem); 278 if (oiddata != NULL) { 279 i = PORT_Strlen(oiddata->desc); 280 if ((prettyColumn + 1 + (i / 3)) > RIGHT_MARGIN) { 281 rv = prettyNewline(out); 282 if (rv < 0) 283 return rv; 284 } 285 286 rv = prettyIndent(out, level); 287 if (rv < 0) 288 return rv; 289 290 rv = fprintf(out, "(%s)", oiddata->desc); 291 if (rv < 0) { 292 PORT_SetError(SEC_ERROR_IO); 293 return rv; 294 } 295 } 296 297 rv = prettyNewline(out); 298 if (rv < 0) 299 return rv; 300 301 if (raw) { 302 rv = prettyPrintLeaf(out, data, len, level); 303 if (rv < 0) 304 return rv; 305 } 306 307 return 0; 308 } 309 310 static char *prettyTagType[32] = { 311 "End of Contents", 312 "Boolean", 313 "Integer", 314 "Bit String", 315 "Octet String", 316 "NULL", 317 "Object Identifier", 318 "0x07", 319 "0x08", 320 "0x09", 321 "Enumerated", 322 "0x0B", 323 "UTF8 String", 324 "0x0D", 325 "0x0E", 326 "0x0F", 327 "Sequence", 328 "Set", 329 "0x12", 330 "Printable String", 331 "T61 String", 332 "0x15", 333 "IA5 String", 334 "UTC Time", 335 "Generalized Time", 336 "0x19", 337 "Visible String", 338 "0x1B", 339 "Universal String", 340 "0x1D", 341 "BMP String", 342 "High-Tag-Number" 343 }; 344 345 static int 346 prettyPrintTag(FILE *out, const unsigned char *src, const unsigned char *end, 347 unsigned char *codep, unsigned int level, PRBool raw) 348 { 349 int rv; 350 unsigned char code, tagnum; 351 352 if (src >= end) { 353 PORT_SetError(SEC_ERROR_BAD_DER); 354 return -1; 355 } 356 357 code = *src; 358 tagnum = code & SEC_ASN1_TAGNUM_MASK; 359 360 /* 361 * NOTE: This code does not (yet) handle the high-tag-number form! 362 */ 363 if (tagnum == SEC_ASN1_HIGH_TAG_NUMBER) { 364 PORT_SetError(SEC_ERROR_BAD_DER); 365 return -1; 366 } 367 368 if (raw) 369 rv = prettyPrintByte(out, code, level); 370 else 371 rv = prettyIndent(out, level); 372 373 if (rv < 0) 374 return rv; 375 376 if (code & SEC_ASN1_CONSTRUCTED) { 377 rv = fprintf(out, "C-"); 378 if (rv < 0) { 379 PORT_SetError(SEC_ERROR_IO); 380 return rv; 381 } 382 } 383 384 switch (code & SEC_ASN1_CLASS_MASK) { 385 case SEC_ASN1_UNIVERSAL: 386 rv = fprintf(out, "%s ", prettyTagType[tagnum]); 387 break; 388 case SEC_ASN1_APPLICATION: 389 rv = fprintf(out, "Application: %d ", tagnum); 390 break; 391 case SEC_ASN1_CONTEXT_SPECIFIC: 392 rv = fprintf(out, "[%d] ", tagnum); 393 break; 394 case SEC_ASN1_PRIVATE: 395 rv = fprintf(out, "Private: %d ", tagnum); 396 break; 397 } 398 399 if (rv < 0) { 400 PORT_SetError(SEC_ERROR_IO); 401 return rv; 402 } 403 404 *codep = code; 405 406 return 1; 407 } 408 409 static int 410 prettyPrintLength(FILE *out, const unsigned char *data, const unsigned char *end, 411 int *lenp, PRBool *indefinitep, unsigned int lv, PRBool raw) 412 { 413 unsigned char lbyte; 414 int lenLen; 415 int rv; 416 417 if (data >= end) { 418 PORT_SetError(SEC_ERROR_BAD_DER); 419 return -1; 420 } 421 422 rv = fprintf(out, " "); 423 if (rv < 0) { 424 PORT_SetError(SEC_ERROR_IO); 425 return rv; 426 } 427 428 *indefinitep = PR_FALSE; 429 430 lbyte = *data++; 431 lenLen = 1; 432 if (lbyte >= 0x80) { 433 /* Multibyte length */ 434 unsigned nb = (unsigned)(lbyte & 0x7f); 435 if (nb > 4) { 436 PORT_SetError(SEC_ERROR_BAD_DER); 437 return -1; 438 } 439 if (nb > 0) { 440 int il; 441 442 if ((data + nb) > end) { 443 PORT_SetError(SEC_ERROR_BAD_DER); 444 return -1; 445 } 446 il = getInteger256(data, nb); 447 if (il < 0) 448 return -1; 449 *lenp = (unsigned)il; 450 } else { 451 *lenp = 0; 452 *indefinitep = PR_TRUE; 453 } 454 lenLen += nb; 455 if (raw) { 456 unsigned int i; 457 458 rv = prettyPrintByte(out, lbyte, lv); 459 if (rv < 0) 460 return rv; 461 for (i = 0; i < nb; i++) { 462 rv = prettyPrintByte(out, data[i], lv); 463 if (rv < 0) 464 return rv; 465 } 466 } 467 } else { 468 *lenp = lbyte; 469 if (raw) { 470 rv = prettyPrintByte(out, lbyte, lv); 471 if (rv < 0) 472 return rv; 473 } 474 } 475 if (*indefinitep) 476 rv = fprintf(out, "(indefinite)\n"); 477 else 478 rv = fprintf(out, "(%d)\n", *lenp); 479 if (rv < 0) { 480 PORT_SetError(SEC_ERROR_IO); 481 return rv; 482 } 483 484 prettyColumn = -1; 485 return lenLen; 486 } 487 488 static int 489 prettyPrintItem(FILE *out, const unsigned char *data, const unsigned char *end, 490 unsigned int lv, PRBool raw) 491 { 492 int slen; 493 int lenLen; 494 const unsigned char *orig = data; 495 int rv; 496 497 while (data < end) { 498 unsigned char code; 499 PRBool indefinite; 500 501 slen = prettyPrintTag(out, data, end, &code, lv, raw); 502 if (slen < 0) 503 return slen; 504 data += slen; 505 506 lenLen = prettyPrintLength(out, data, end, &slen, &indefinite, lv, raw); 507 if (lenLen < 0) 508 return lenLen; 509 data += lenLen; 510 511 /* 512 * Just quit now if slen more bytes puts us off the end. 513 */ 514 if (data > end || slen > (end - data)) { 515 PORT_SetError(SEC_ERROR_BAD_DER); 516 return -1; 517 } 518 519 if (code & SEC_ASN1_CONSTRUCTED) { 520 if (slen > 0 || indefinite) { 521 slen = prettyPrintItem(out, data, 522 slen == 0 ? end : data + slen, 523 lv + 1, raw); 524 if (slen < 0) 525 return slen; 526 data += slen; 527 } 528 } else if (code == 0) { 529 if (slen != 0 || lenLen != 1) { 530 PORT_SetError(SEC_ERROR_BAD_DER); 531 return -1; 532 } 533 break; 534 } else { 535 switch (code) { 536 case SEC_ASN1_PRINTABLE_STRING: 537 case SEC_ASN1_IA5_STRING: 538 case SEC_ASN1_VISIBLE_STRING: 539 rv = prettyPrintString(out, data, slen, lv + 1, raw); 540 if (rv < 0) 541 return rv; 542 break; 543 case SEC_ASN1_UTC_TIME: 544 rv = prettyPrintTime(out, data, slen, lv + 1, raw, PR_TRUE); 545 if (rv < 0) 546 return rv; 547 break; 548 case SEC_ASN1_GENERALIZED_TIME: 549 rv = prettyPrintTime(out, data, slen, lv + 1, raw, PR_FALSE); 550 if (rv < 0) 551 return rv; 552 break; 553 case SEC_ASN1_OBJECT_ID: 554 rv = prettyPrintObjectID(out, data, slen, lv + 1, raw); 555 if (rv < 0) 556 return rv; 557 break; 558 case SEC_ASN1_BOOLEAN: /* could do nicer job */ 559 case SEC_ASN1_INTEGER: /* could do nicer job */ 560 case SEC_ASN1_BIT_STRING: /* could do nicer job */ 561 case SEC_ASN1_OCTET_STRING: 562 case SEC_ASN1_NULL: 563 case SEC_ASN1_ENUMERATED: /* could do nicer job, as INTEGER */ 564 case SEC_ASN1_UTF8_STRING: 565 case SEC_ASN1_T61_STRING: /* print as printable string? */ 566 case SEC_ASN1_UNIVERSAL_STRING: 567 case SEC_ASN1_BMP_STRING: 568 default: 569 rv = prettyPrintLeaf(out, data, slen, lv + 1); 570 if (rv < 0) 571 return rv; 572 break; 573 } 574 data += slen; 575 } 576 } 577 578 rv = prettyNewline(out); 579 if (rv < 0) 580 return rv; 581 582 return data - orig; 583 } 584 585 SECStatus 586 DER_PrettyPrint(FILE *out, const SECItem *it, PRBool raw) 587 { 588 int rv; 589 590 prettyColumn = -1; 591 592 rv = prettyPrintItem(out, it->data, it->data + it->len, 0, raw); 593 if (rv < 0) 594 return SECFailure; 595 return SECSuccess; 596 }