Back to index

lightning-sunbird  0.9+nobinonly
derprint.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 #include "secutil.h"
00037 #include "secoid.h"
00038 
00039 #ifdef __sun
00040 extern int fprintf(FILE *strm, const char *format, .../* args */);
00041 extern int fflush(FILE *stream);
00042 #endif
00043 
00044 #define RIGHT_MARGIN 24
00045 /*#define RAW_BYTES 1 */
00046 
00047 static int prettyColumn = 0;
00048 
00049 static int
00050 getInteger256(unsigned char *data, unsigned int nb)
00051 {
00052     int val;
00053 
00054     switch (nb) {
00055       case 1:
00056        val = data[0];
00057        break;
00058       case 2:
00059        val = (data[0] << 8) | data[1];
00060        break;
00061       case 3:
00062        val = (data[0] << 16) | (data[1] << 8) | data[2];
00063        break;
00064       case 4:
00065        val = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
00066        break;
00067       default:
00068        PORT_SetError(SEC_ERROR_BAD_DER);
00069        return -1;
00070     }
00071 
00072     return val;
00073 }
00074 
00075 static int
00076 prettyNewline(FILE *out)
00077 {
00078     int rv;
00079 
00080     if (prettyColumn != -1) {
00081        rv = fprintf(out, "\n");
00082        prettyColumn = -1;
00083        if (rv < 0) {
00084            PORT_SetError(SEC_ERROR_IO);
00085            return rv;
00086        }
00087     }
00088     return 0;
00089 }
00090 
00091 static int
00092 prettyIndent(FILE *out, unsigned level)
00093 {
00094     unsigned int i;
00095     int rv;
00096 
00097     if (prettyColumn == -1) {
00098        prettyColumn = level;
00099        for (i = 0; i < level; i++) {
00100            rv = fprintf(out, "   ");
00101            if (rv < 0) {
00102               PORT_SetError(SEC_ERROR_IO);
00103               return rv;
00104            }
00105        }
00106     }
00107 
00108     return 0;
00109 }
00110 
00111 static int
00112 prettyPrintByte(FILE *out, unsigned char item, unsigned int level)
00113 {
00114     int rv;
00115 
00116     rv = prettyIndent(out, level);
00117     if (rv < 0)
00118        return rv;
00119 
00120     rv = fprintf(out, "%02x ", item);
00121     if (rv < 0) {
00122        PORT_SetError(SEC_ERROR_IO);
00123        return rv;
00124     }
00125 
00126     prettyColumn++;
00127     if (prettyColumn >= RIGHT_MARGIN) {
00128        return prettyNewline(out);
00129     }
00130 
00131     return 0;
00132 }
00133 
00134 static int
00135 prettyPrintLeaf(FILE *out, unsigned char *data,
00136               unsigned int len, unsigned int lv)
00137 {
00138     unsigned int i;
00139     int rv;
00140 
00141     for (i = 0; i < len; i++) {
00142        rv = prettyPrintByte(out, *data++, lv);
00143        if (rv < 0)
00144            return rv;
00145     }
00146     return prettyNewline(out);
00147 }
00148 
00149 static int
00150 prettyPrintStringStart(FILE *out, unsigned char *str,
00151                      unsigned int len, unsigned int level)
00152 {
00153 #define BUF_SIZE 100
00154     unsigned char buf[BUF_SIZE];
00155     int rv;
00156 
00157     if (len >= BUF_SIZE)
00158        len = BUF_SIZE - 1;
00159 
00160     rv = prettyNewline(out);
00161     if (rv < 0)
00162        return rv;
00163 
00164     rv = prettyIndent(out, level);
00165     if (rv < 0)
00166        return rv;
00167 
00168     memcpy(buf, str, len);
00169     buf[len] = '\000';
00170 
00171     rv = fprintf(out, "\"%s\"", buf);
00172     if (rv < 0) {
00173        PORT_SetError(SEC_ERROR_IO);
00174        return rv;
00175     }
00176 
00177     return 0;
00178 #undef BUF_SIZE
00179 }
00180 
00181 static int
00182 prettyPrintString(FILE *out, unsigned char *str,
00183                 unsigned int len, unsigned int level, PRBool raw)
00184 {
00185     int rv;
00186 
00187     rv = prettyPrintStringStart(out, str, len, level);
00188     if (rv < 0)
00189        return rv;
00190 
00191     rv = prettyNewline(out);
00192     if (rv < 0)
00193        return rv;
00194 
00195     if (raw) {
00196        rv = prettyPrintLeaf(out, str, len, level);
00197        if (rv < 0)
00198            return rv;
00199     }
00200 
00201     return 0;
00202 }
00203 
00204 static int
00205 prettyPrintTime(FILE *out, unsigned char *str,
00206               unsigned int len, unsigned int level, PRBool raw, PRBool utc)
00207 {
00208     SECItem time_item;
00209     int rv;
00210 
00211     rv = prettyPrintStringStart(out, str, len, level);
00212     if (rv < 0)
00213        return rv;
00214 
00215     time_item.data = str;
00216     time_item.len = len;
00217 
00218     rv = fprintf(out, " (");
00219     if (rv < 0) {
00220        PORT_SetError(SEC_ERROR_IO);
00221        return rv;
00222     }
00223 
00224     if (utc)
00225        SECU_PrintUTCTime(out, &time_item, NULL, 0);
00226     else
00227        SECU_PrintGeneralizedTime(out, &time_item, NULL, 0);
00228 
00229     rv = fprintf(out, ")");
00230     if (rv < 0) {
00231        PORT_SetError(SEC_ERROR_IO);
00232        return rv;
00233     }
00234 
00235     rv = prettyNewline(out);
00236     if (rv < 0)
00237        return rv;
00238 
00239     if (raw) {
00240        rv = prettyPrintLeaf(out, str, len, level);
00241        if (rv < 0)
00242            return rv;
00243     }
00244 
00245     return 0;
00246 }
00247 
00248 static int
00249 prettyPrintObjectID(FILE *out, unsigned char *data,
00250                   unsigned int len, unsigned int level, PRBool raw)
00251 {
00252     SECOidData *oiddata;
00253     SECItem oiditem;
00254     unsigned int i;
00255     unsigned long val;
00256     int rv;
00257 
00258 
00259     /*
00260      * First print the Object Id in numeric format
00261      */
00262 
00263     rv = prettyIndent(out, level);
00264     if (rv < 0)
00265        return rv;
00266 
00267     val = data[0];
00268     i   = val % 40;
00269     val = val / 40;
00270     rv = fprintf(out, "%lu %u ", val, i);
00271     if (rv < 0) {
00272        PORT_SetError(SEC_ERROR_IO);
00273        return rv;
00274     }
00275 
00276     val = 0;
00277     for (i = 1; i < len; ++i) {
00278         unsigned long j;
00279 
00280        j = data[i];
00281        val = (val << 7) | (j & 0x7f);
00282        if (j & 0x80) 
00283            continue;
00284        rv = fprintf(out, "%lu ", val);
00285        if (rv < 0) {
00286            PORT_SetError(SEC_ERROR_IO);
00287            return rv;
00288        }
00289        val = 0;
00290     }
00291 
00292     /*
00293      * Now try to look it up and print a symbolic version.
00294      */
00295     oiditem.data = data;
00296     oiditem.len = len;
00297     oiddata = SECOID_FindOID(&oiditem);
00298     if (oiddata != NULL) {
00299        i = PORT_Strlen(oiddata->desc);
00300        if ((prettyColumn + 1 + (i / 3)) > RIGHT_MARGIN) {
00301            rv = prettyNewline(out);
00302            if (rv < 0)
00303               return rv;
00304        }
00305 
00306        rv = prettyIndent(out, level);
00307        if (rv < 0)
00308            return rv;
00309 
00310        rv = fprintf(out, "(%s)", oiddata->desc);
00311        if (rv < 0) {
00312            PORT_SetError(SEC_ERROR_IO);
00313            return rv;
00314        }
00315     }
00316 
00317     /*
00318      * Finally, on a new line, print the raw bytes (if requested).
00319      */
00320     if (raw) {
00321        rv = prettyNewline(out);
00322        if (rv < 0) {
00323            PORT_SetError(SEC_ERROR_IO);
00324            return rv;
00325        }
00326 
00327        for (i = 0; i < len; i++) {
00328            rv = prettyPrintByte(out, *data++, level);
00329            if (rv < 0)
00330               return rv;
00331        }
00332     }
00333 
00334     return prettyNewline(out);
00335 }
00336 
00337 static char *prettyTagType [32] = {
00338   "End of Contents",
00339   "Boolean",
00340   "Integer",
00341   "Bit String",
00342   "Octet String",
00343   "NULL",
00344   "Object Identifier",
00345   "0x07",
00346   "0x08",
00347   "0x09",
00348   "Enumerated",
00349   "0x0B",
00350   "UTF8 String",
00351   "0x0D",
00352   "0x0E",
00353   "0x0F",
00354   "Sequence",
00355   "Set",
00356   "0x12",
00357   "Printable String",
00358   "T61 String",
00359   "0x15",
00360   "IA5 String",
00361   "UTC Time",
00362   "Generalized Time",
00363   "0x19",
00364   "Visible String",
00365   "0x1B",
00366   "Universal String",
00367   "0x1D",
00368   "BMP String",
00369   "High-Tag-Number"
00370 };
00371 
00372 static int
00373 prettyPrintTag(FILE *out, unsigned char *src, unsigned char *end,
00374               unsigned char *codep, unsigned int level, PRBool raw)
00375 {
00376     int rv;
00377     unsigned char code, tagnum;
00378 
00379     if (src >= end) {
00380        PORT_SetError(SEC_ERROR_BAD_DER);
00381        return -1;
00382     }
00383 
00384     code = *src;
00385     tagnum = code & SEC_ASN1_TAGNUM_MASK;
00386 
00387     /*
00388      * NOTE: This code does not (yet) handle the high-tag-number form!
00389      */
00390     if (tagnum == SEC_ASN1_HIGH_TAG_NUMBER) {
00391         PORT_SetError(SEC_ERROR_BAD_DER);
00392        return -1;
00393     }
00394 
00395     if (raw)
00396        rv = prettyPrintByte(out, code, level);
00397     else
00398        rv = prettyIndent(out, level);
00399 
00400     if (rv < 0)
00401        return rv;
00402 
00403     if (code & SEC_ASN1_CONSTRUCTED) {
00404         rv = fprintf(out, "C-");
00405        if (rv < 0) {
00406            PORT_SetError(SEC_ERROR_IO);
00407            return rv;
00408        }
00409     }
00410 
00411     switch (code & SEC_ASN1_CLASS_MASK) {
00412     case SEC_ASN1_UNIVERSAL:
00413         rv = fprintf(out, "%s ", prettyTagType[tagnum]);
00414        break;
00415     case SEC_ASN1_APPLICATION:
00416         rv = fprintf(out, "Application: %d ", tagnum);
00417        break;
00418     case SEC_ASN1_CONTEXT_SPECIFIC:
00419         rv = fprintf(out, "[%d] ", tagnum);
00420        break;
00421     case SEC_ASN1_PRIVATE:
00422         rv = fprintf(out, "Private: %d ", tagnum);
00423        break;
00424     }
00425 
00426     if (rv < 0) {
00427         PORT_SetError(SEC_ERROR_IO);
00428        return rv;
00429     }
00430 
00431     *codep = code;
00432 
00433     return 1;
00434 }
00435 
00436 static int
00437 prettyPrintLength(FILE *out, unsigned char *data, unsigned char *end,
00438                 int *lenp, PRBool *indefinitep, unsigned int lv, PRBool raw)
00439 {
00440     unsigned char lbyte;
00441     int lenLen;
00442     int rv;
00443 
00444     if (data >= end) {
00445        PORT_SetError(SEC_ERROR_BAD_DER);
00446        return -1;
00447     }
00448 
00449     rv = fprintf(out, " ");
00450     if (rv < 0) {
00451         PORT_SetError(SEC_ERROR_IO);
00452        return rv;
00453     }
00454 
00455     *indefinitep = PR_FALSE;
00456 
00457     lbyte = *data++;
00458     if (lbyte >= 0x80) {
00459        /* Multibyte length */
00460        unsigned nb = (unsigned) (lbyte & 0x7f);
00461        if (nb > 4) {
00462            PORT_SetError(SEC_ERROR_BAD_DER);
00463            return -1;
00464        }
00465        if (nb > 0) {
00466            int il;
00467 
00468            if ((data + nb) > end) {
00469               PORT_SetError(SEC_ERROR_BAD_DER);
00470               return -1;
00471            }
00472            il = getInteger256(data, nb);
00473            if (il < 0) return -1;
00474            *lenp = (unsigned) il;
00475        } else {
00476            *lenp = 0;
00477            *indefinitep = PR_TRUE;
00478        }
00479        lenLen = nb + 1;
00480        if (raw) {
00481            int i;
00482 
00483            rv = prettyPrintByte(out, lbyte, lv);
00484            if (rv < 0)
00485               return rv;
00486            for (i = 0; i < nb; i++) {
00487               rv = prettyPrintByte(out, data[i], lv);
00488               if (rv < 0)
00489                   return rv;
00490            }
00491        }
00492     } else {
00493        *lenp = lbyte;
00494        lenLen = 1;
00495        if (raw) {
00496            rv = prettyPrintByte(out, lbyte, lv);
00497            if (rv < 0)
00498               return rv;
00499        }
00500     }
00501     if (*indefinitep)
00502        rv = fprintf(out, "(indefinite)\n");
00503     else
00504        rv = fprintf(out, "(%d)\n", *lenp);
00505     if (rv < 0) {
00506         PORT_SetError(SEC_ERROR_IO);
00507        return rv;
00508     }
00509 
00510     prettyColumn = -1;
00511     return lenLen;
00512 }
00513 
00514 static int
00515 prettyPrintItem(FILE *out, unsigned char *data, unsigned char *end,
00516               unsigned int lv, PRBool raw)
00517 {
00518     int slen;
00519     int lenLen;
00520     unsigned char *orig = data;
00521     int rv;
00522 
00523     while (data < end) {
00524         unsigned char code;
00525        PRBool indefinite;
00526 
00527        slen = prettyPrintTag(out, data, end, &code, lv, raw);
00528        if (slen < 0)
00529            return slen;
00530        data += slen;
00531 
00532        lenLen = prettyPrintLength(out, data, end, &slen, &indefinite, lv, raw);
00533        if (lenLen < 0)
00534            return lenLen;
00535        data += lenLen;
00536 
00537        /*
00538         * Just quit now if slen more bytes puts us off the end.
00539         */
00540        if ((data + slen) > end) {
00541            PORT_SetError(SEC_ERROR_BAD_DER);
00542            return -1;
00543        }
00544 
00545         if (code & SEC_ASN1_CONSTRUCTED) {
00546            if (slen > 0 || indefinite) {
00547               slen = prettyPrintItem(out, data,
00548                                    slen == 0 ? end : data + slen,
00549                                    lv+1, raw);
00550               if (slen < 0)
00551                   return slen;
00552               data += slen;
00553            }
00554        } else if (code == 0) {
00555            if (slen != 0 || lenLen != 1) {
00556               PORT_SetError(SEC_ERROR_BAD_DER);
00557               return -1;
00558            }
00559            break;
00560        } else {
00561            switch (code) {
00562              case SEC_ASN1_PRINTABLE_STRING:
00563              case SEC_ASN1_IA5_STRING:
00564              case SEC_ASN1_VISIBLE_STRING:
00565                rv = prettyPrintString(out, data, slen, lv+1, raw);
00566               if (rv < 0)
00567                   return rv;
00568               break;
00569              case SEC_ASN1_UTC_TIME:
00570                rv = prettyPrintTime(out, data, slen, lv+1, raw, PR_TRUE);
00571               if (rv < 0)
00572                   return rv;
00573               break;
00574              case SEC_ASN1_GENERALIZED_TIME:
00575                rv = prettyPrintTime(out, data, slen, lv+1, raw, PR_FALSE);
00576               if (rv < 0)
00577                   return rv;
00578               break;
00579              case SEC_ASN1_OBJECT_ID:
00580                rv = prettyPrintObjectID(out, data, slen, lv+1, raw);
00581               if (rv < 0)
00582                   return rv;
00583               break;
00584              case SEC_ASN1_BOOLEAN:       /* could do nicer job */
00585              case SEC_ASN1_INTEGER:       /* could do nicer job */
00586              case SEC_ASN1_BIT_STRING:    /* could do nicer job */
00587              case SEC_ASN1_OCTET_STRING:
00588              case SEC_ASN1_NULL:
00589              case SEC_ASN1_ENUMERATED:    /* could do nicer job, as INTEGER */
00590              case SEC_ASN1_UTF8_STRING:
00591              case SEC_ASN1_T61_STRING:    /* print as printable string? */
00592              case SEC_ASN1_UNIVERSAL_STRING:
00593              case SEC_ASN1_BMP_STRING:
00594              default:
00595                rv = prettyPrintLeaf(out, data, slen, lv+1);
00596               if (rv < 0)
00597                   return rv;
00598               break;
00599            }
00600            data += slen;
00601        }
00602     }
00603 
00604     rv = prettyNewline(out);
00605     if (rv < 0)
00606        return rv;
00607 
00608     return data - orig;
00609 }
00610 
00611 SECStatus
00612 DER_PrettyPrint(FILE *out, SECItem *it, PRBool raw)
00613 {
00614     int rv;
00615 
00616     prettyColumn = -1;
00617 
00618     rv = prettyPrintItem(out, it->data, it->data + it->len, 0, raw);
00619     if (rv < 0)
00620        return SECFailure;
00621     return SECSuccess;
00622 }