mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 21:55:31 +00:00
620 lines
12 KiB
C
620 lines
12 KiB
C
/*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Netscape security libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU General Public License Version 2 or later (the
|
|
* "GPL"), in which case the provisions of the GPL are applicable
|
|
* instead of those above. If you wish to allow use of your
|
|
* version of this file only under the terms of the GPL and not to
|
|
* allow others to use your version of this file under the MPL,
|
|
* indicate your decision by deleting the provisions above and
|
|
* replace them with the notice and other provisions required by
|
|
* the GPL. If you do not delete the provisions above, a recipient
|
|
* may use your version of this file under either the MPL or the
|
|
* GPL.
|
|
*/
|
|
#include "secutil.h"
|
|
#include "secoid.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(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:
|
|
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, 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, 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, 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, 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 = 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, 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;
|
|
|
|
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 = 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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finally, on a new line, print the raw bytes (if requested).
|
|
*/
|
|
if (raw) {
|
|
rv = prettyNewline(out);
|
|
if (rv < 0) {
|
|
PORT_SetError(SEC_ERROR_IO);
|
|
return rv;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
rv = prettyPrintByte(out, *data++, level);
|
|
if (rv < 0)
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return prettyNewline(out);
|
|
}
|
|
|
|
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, unsigned char *src, 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, unsigned char *data, 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++;
|
|
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 + 1;
|
|
if (raw) {
|
|
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;
|
|
lenLen = 1;
|
|
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, unsigned char *data, unsigned char *end,
|
|
unsigned int lv, PRBool raw)
|
|
{
|
|
int slen;
|
|
int lenLen;
|
|
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 + slen) > end) {
|
|
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, 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;
|
|
}
|