michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "secutil.h" michael@0: #include "secoid.h" michael@0: michael@0: #ifdef __sun michael@0: extern int fprintf(FILE *strm, const char *format, .../* args */); michael@0: extern int fflush(FILE *stream); michael@0: #endif michael@0: michael@0: #define RIGHT_MARGIN 24 michael@0: /*#define RAW_BYTES 1 */ michael@0: michael@0: static int prettyColumn = 0; michael@0: michael@0: static int michael@0: getInteger256(const unsigned char *data, unsigned int nb) michael@0: { michael@0: int val; michael@0: michael@0: switch (nb) { michael@0: case 1: michael@0: val = data[0]; michael@0: break; michael@0: case 2: michael@0: val = (data[0] << 8) | data[1]; michael@0: break; michael@0: case 3: michael@0: val = (data[0] << 16) | (data[1] << 8) | data[2]; michael@0: break; michael@0: case 4: michael@0: val = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; michael@0: break; michael@0: default: michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: michael@0: return val; michael@0: } michael@0: michael@0: static int michael@0: prettyNewline(FILE *out) michael@0: { michael@0: int rv; michael@0: michael@0: if (prettyColumn != -1) { michael@0: rv = fprintf(out, "\n"); michael@0: prettyColumn = -1; michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: prettyIndent(FILE *out, unsigned level) michael@0: { michael@0: unsigned int i; michael@0: int rv; michael@0: michael@0: if (prettyColumn == -1) { michael@0: prettyColumn = level; michael@0: for (i = 0; i < level; i++) { michael@0: rv = fprintf(out, " "); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintByte(FILE *out, unsigned char item, unsigned int level) michael@0: { michael@0: int rv; michael@0: michael@0: rv = prettyIndent(out, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: rv = fprintf(out, "%02x ", item); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: prettyColumn++; michael@0: if (prettyColumn >= RIGHT_MARGIN) { michael@0: return prettyNewline(out); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintLeaf(FILE *out, const unsigned char *data, michael@0: unsigned int len, unsigned int lv) michael@0: { michael@0: unsigned int i; michael@0: int rv; michael@0: michael@0: for (i = 0; i < len; i++) { michael@0: rv = prettyPrintByte(out, *data++, lv); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: return prettyNewline(out); michael@0: } michael@0: michael@0: static int michael@0: prettyPrintStringStart(FILE *out, const unsigned char *str, michael@0: unsigned int len, unsigned int level) michael@0: { michael@0: #define BUF_SIZE 100 michael@0: unsigned char buf[BUF_SIZE]; michael@0: int rv; michael@0: michael@0: if (len >= BUF_SIZE) michael@0: len = BUF_SIZE - 1; michael@0: michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: rv = prettyIndent(out, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: memcpy(buf, str, len); michael@0: buf[len] = '\000'; michael@0: michael@0: rv = fprintf(out, "\"%s\"", buf); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: return 0; michael@0: #undef BUF_SIZE michael@0: } michael@0: michael@0: static int michael@0: prettyPrintString(FILE *out, const unsigned char *str, michael@0: unsigned int len, unsigned int level, PRBool raw) michael@0: { michael@0: int rv; michael@0: michael@0: rv = prettyPrintStringStart(out, str, len, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: if (raw) { michael@0: rv = prettyPrintLeaf(out, str, len, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintTime(FILE *out, const unsigned char *str, michael@0: unsigned int len, unsigned int level, PRBool raw, PRBool utc) michael@0: { michael@0: SECItem time_item; michael@0: int rv; michael@0: michael@0: rv = prettyPrintStringStart(out, str, len, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: time_item.data = (unsigned char *)str; michael@0: time_item.len = len; michael@0: michael@0: rv = fprintf(out, " ("); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: if (utc) michael@0: SECU_PrintUTCTime(out, &time_item, NULL, 0); michael@0: else michael@0: SECU_PrintGeneralizedTime(out, &time_item, NULL, 0); michael@0: michael@0: rv = fprintf(out, ")"); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: if (raw) { michael@0: rv = prettyPrintLeaf(out, str, len, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintObjectID(FILE *out, const unsigned char *data, michael@0: unsigned int len, unsigned int level, PRBool raw) michael@0: { michael@0: SECOidData *oiddata; michael@0: SECItem oiditem; michael@0: unsigned int i; michael@0: unsigned long val; michael@0: int rv; michael@0: michael@0: michael@0: /* michael@0: * First print the Object Id in numeric format michael@0: */ michael@0: michael@0: rv = prettyIndent(out, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: val = data[0]; michael@0: i = val % 40; michael@0: val = val / 40; michael@0: rv = fprintf(out, "%lu %u ", val, i); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: val = 0; michael@0: for (i = 1; i < len; ++i) { michael@0: unsigned long j; michael@0: michael@0: j = data[i]; michael@0: val = (val << 7) | (j & 0x7f); michael@0: if (j & 0x80) michael@0: continue; michael@0: rv = fprintf(out, "%lu ", val); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: val = 0; michael@0: } michael@0: michael@0: /* michael@0: * Now try to look it up and print a symbolic version. michael@0: */ michael@0: oiditem.data = (unsigned char *)data; michael@0: oiditem.len = len; michael@0: oiddata = SECOID_FindOID(&oiditem); michael@0: if (oiddata != NULL) { michael@0: i = PORT_Strlen(oiddata->desc); michael@0: if ((prettyColumn + 1 + (i / 3)) > RIGHT_MARGIN) { michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: michael@0: rv = prettyIndent(out, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: rv = fprintf(out, "(%s)", oiddata->desc); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Finally, on a new line, print the raw bytes (if requested). michael@0: */ michael@0: if (raw) { michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: for (i = 0; i < len; i++) { michael@0: rv = prettyPrintByte(out, *data++, level); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return prettyNewline(out); michael@0: } michael@0: michael@0: static char *prettyTagType [32] = { michael@0: "End of Contents", michael@0: "Boolean", michael@0: "Integer", michael@0: "Bit String", michael@0: "Octet String", michael@0: "NULL", michael@0: "Object Identifier", michael@0: "0x07", michael@0: "0x08", michael@0: "0x09", michael@0: "Enumerated", michael@0: "0x0B", michael@0: "UTF8 String", michael@0: "0x0D", michael@0: "0x0E", michael@0: "0x0F", michael@0: "Sequence", michael@0: "Set", michael@0: "0x12", michael@0: "Printable String", michael@0: "T61 String", michael@0: "0x15", michael@0: "IA5 String", michael@0: "UTC Time", michael@0: "Generalized Time", michael@0: "0x19", michael@0: "Visible String", michael@0: "0x1B", michael@0: "Universal String", michael@0: "0x1D", michael@0: "BMP String", michael@0: "High-Tag-Number" michael@0: }; michael@0: michael@0: static int michael@0: prettyPrintTag(FILE *out, const unsigned char *src, const unsigned char *end, michael@0: unsigned char *codep, unsigned int level, PRBool raw) michael@0: { michael@0: int rv; michael@0: unsigned char code, tagnum; michael@0: michael@0: if (src >= end) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: michael@0: code = *src; michael@0: tagnum = code & SEC_ASN1_TAGNUM_MASK; michael@0: michael@0: /* michael@0: * NOTE: This code does not (yet) handle the high-tag-number form! michael@0: */ michael@0: if (tagnum == SEC_ASN1_HIGH_TAG_NUMBER) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: michael@0: if (raw) michael@0: rv = prettyPrintByte(out, code, level); michael@0: else michael@0: rv = prettyIndent(out, level); michael@0: michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: if (code & SEC_ASN1_CONSTRUCTED) { michael@0: rv = fprintf(out, "C-"); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: switch (code & SEC_ASN1_CLASS_MASK) { michael@0: case SEC_ASN1_UNIVERSAL: michael@0: rv = fprintf(out, "%s ", prettyTagType[tagnum]); michael@0: break; michael@0: case SEC_ASN1_APPLICATION: michael@0: rv = fprintf(out, "Application: %d ", tagnum); michael@0: break; michael@0: case SEC_ASN1_CONTEXT_SPECIFIC: michael@0: rv = fprintf(out, "[%d] ", tagnum); michael@0: break; michael@0: case SEC_ASN1_PRIVATE: michael@0: rv = fprintf(out, "Private: %d ", tagnum); michael@0: break; michael@0: } michael@0: michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: *codep = code; michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintLength(FILE *out, const unsigned char *data, const unsigned char *end, michael@0: int *lenp, PRBool *indefinitep, unsigned int lv, PRBool raw) michael@0: { michael@0: unsigned char lbyte; michael@0: int lenLen; michael@0: int rv; michael@0: michael@0: if (data >= end) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: michael@0: rv = fprintf(out, " "); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: *indefinitep = PR_FALSE; michael@0: michael@0: lbyte = *data++; michael@0: if (lbyte >= 0x80) { michael@0: /* Multibyte length */ michael@0: unsigned nb = (unsigned) (lbyte & 0x7f); michael@0: if (nb > 4) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: if (nb > 0) { michael@0: int il; michael@0: michael@0: if ((data + nb) > end) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: il = getInteger256(data, nb); michael@0: if (il < 0) return -1; michael@0: *lenp = (unsigned) il; michael@0: } else { michael@0: *lenp = 0; michael@0: *indefinitep = PR_TRUE; michael@0: } michael@0: lenLen = nb + 1; michael@0: if (raw) { michael@0: int i; michael@0: michael@0: rv = prettyPrintByte(out, lbyte, lv); michael@0: if (rv < 0) michael@0: return rv; michael@0: for (i = 0; i < nb; i++) { michael@0: rv = prettyPrintByte(out, data[i], lv); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: } michael@0: } else { michael@0: *lenp = lbyte; michael@0: lenLen = 1; michael@0: if (raw) { michael@0: rv = prettyPrintByte(out, lbyte, lv); michael@0: if (rv < 0) michael@0: return rv; michael@0: } michael@0: } michael@0: if (*indefinitep) michael@0: rv = fprintf(out, "(indefinite)\n"); michael@0: else michael@0: rv = fprintf(out, "(%d)\n", *lenp); michael@0: if (rv < 0) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return rv; michael@0: } michael@0: michael@0: prettyColumn = -1; michael@0: return lenLen; michael@0: } michael@0: michael@0: static int michael@0: prettyPrintItem(FILE *out, const unsigned char *data, const unsigned char *end, michael@0: unsigned int lv, PRBool raw) michael@0: { michael@0: int slen; michael@0: int lenLen; michael@0: const unsigned char *orig = data; michael@0: int rv; michael@0: michael@0: while (data < end) { michael@0: unsigned char code; michael@0: PRBool indefinite; michael@0: michael@0: slen = prettyPrintTag(out, data, end, &code, lv, raw); michael@0: if (slen < 0) michael@0: return slen; michael@0: data += slen; michael@0: michael@0: lenLen = prettyPrintLength(out, data, end, &slen, &indefinite, lv, raw); michael@0: if (lenLen < 0) michael@0: return lenLen; michael@0: data += lenLen; michael@0: michael@0: /* michael@0: * Just quit now if slen more bytes puts us off the end. michael@0: */ michael@0: if ((data + slen) > end) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: michael@0: if (code & SEC_ASN1_CONSTRUCTED) { michael@0: if (slen > 0 || indefinite) { michael@0: slen = prettyPrintItem(out, data, michael@0: slen == 0 ? end : data + slen, michael@0: lv+1, raw); michael@0: if (slen < 0) michael@0: return slen; michael@0: data += slen; michael@0: } michael@0: } else if (code == 0) { michael@0: if (slen != 0 || lenLen != 1) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return -1; michael@0: } michael@0: break; michael@0: } else { michael@0: switch (code) { michael@0: case SEC_ASN1_PRINTABLE_STRING: michael@0: case SEC_ASN1_IA5_STRING: michael@0: case SEC_ASN1_VISIBLE_STRING: michael@0: rv = prettyPrintString(out, data, slen, lv+1, raw); michael@0: if (rv < 0) michael@0: return rv; michael@0: break; michael@0: case SEC_ASN1_UTC_TIME: michael@0: rv = prettyPrintTime(out, data, slen, lv+1, raw, PR_TRUE); michael@0: if (rv < 0) michael@0: return rv; michael@0: break; michael@0: case SEC_ASN1_GENERALIZED_TIME: michael@0: rv = prettyPrintTime(out, data, slen, lv+1, raw, PR_FALSE); michael@0: if (rv < 0) michael@0: return rv; michael@0: break; michael@0: case SEC_ASN1_OBJECT_ID: michael@0: rv = prettyPrintObjectID(out, data, slen, lv+1, raw); michael@0: if (rv < 0) michael@0: return rv; michael@0: break; michael@0: case SEC_ASN1_BOOLEAN: /* could do nicer job */ michael@0: case SEC_ASN1_INTEGER: /* could do nicer job */ michael@0: case SEC_ASN1_BIT_STRING: /* could do nicer job */ michael@0: case SEC_ASN1_OCTET_STRING: michael@0: case SEC_ASN1_NULL: michael@0: case SEC_ASN1_ENUMERATED: /* could do nicer job, as INTEGER */ michael@0: case SEC_ASN1_UTF8_STRING: michael@0: case SEC_ASN1_T61_STRING: /* print as printable string? */ michael@0: case SEC_ASN1_UNIVERSAL_STRING: michael@0: case SEC_ASN1_BMP_STRING: michael@0: default: michael@0: rv = prettyPrintLeaf(out, data, slen, lv+1); michael@0: if (rv < 0) michael@0: return rv; michael@0: break; michael@0: } michael@0: data += slen; michael@0: } michael@0: } michael@0: michael@0: rv = prettyNewline(out); michael@0: if (rv < 0) michael@0: return rv; michael@0: michael@0: return data - orig; michael@0: } michael@0: michael@0: SECStatus michael@0: DER_PrettyPrint(FILE *out, const SECItem *it, PRBool raw) michael@0: { michael@0: int rv; michael@0: michael@0: prettyColumn = -1; michael@0: michael@0: rv = prettyPrintItem(out, it->data, it->data + it->len, 0, raw); michael@0: if (rv < 0) michael@0: return SECFailure; michael@0: return SECSuccess; michael@0: }