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