/* 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/. */

/*
** secutil.c - various functions used by security stuff
**
*/

/* pkcs #7 -related functions */

#include "secutil.h"
#include "secpkcs7.h"
#include "secoid.h"
#include <sys/stat.h>
#include <stdarg.h>

#ifdef XP_UNIX
#include <unistd.h>
#endif

/* for SEC_TraverseNames */
#include "cert.h"
#include "prtypes.h"
#include "prtime.h"

#include "prlong.h"
#include "secmod.h"
#include "pk11func.h"
#include "prerror.h"

/*
** PKCS7 Support
*/

/* forward declaration */
int
sv_PrintPKCS7ContentInfo(FILE *, SEC_PKCS7ContentInfo *, char *);

void
sv_PrintAsHex(FILE *out, SECItem *data, char *m)
{
    unsigned i;

    if (m)
        fprintf(out, "%s", m);

    for (i = 0; i < data->len; i++) {
        if (i < data->len - 1) {
            fprintf(out, "%02x:", data->data[i]);
        } else {
            fprintf(out, "%02x\n", data->data[i]);
            break;
        }
    }
}

void
sv_PrintInteger(FILE *out, SECItem *i, char *m)
{
    int iv;

    if (i->len > 4) {
        sv_PrintAsHex(out, i, m);
    } else {
        iv = DER_GetInteger(i);
        fprintf(out, "%s%d (0x%x)\n", m, iv, iv);
    }
}

int
sv_PrintTime(FILE *out, SECItem *t, char *m)
{
    PRExplodedTime printableTime;
    PRTime time;
    char *timeString;
    int rv;

    rv = DER_DecodeTimeChoice(&time, t);
    if (rv)
        return rv;

    /* Convert to local time */
    PR_ExplodeTime(time, PR_LocalTimeParameters, &printableTime);

    timeString = (char *)PORT_Alloc(256);

    if (timeString) {
        if (PR_FormatTime(timeString, 256, "%a %b %d %H:%M:%S %Y", &printableTime)) {
            fprintf(out, "%s%s\n", m, timeString);
        }
        PORT_Free(timeString);
        return 0;
    }
    return SECFailure;
}

int
sv_PrintValidity(FILE *out, CERTValidity *v, char *m)
{
    int rv;

    fprintf(out, "%s", m);
    rv = sv_PrintTime(out, &v->notBefore, "notBefore=");
    if (rv)
        return rv;
    fprintf(out, "%s", m);
    sv_PrintTime(out, &v->notAfter, "notAfter=");
    return rv;
}

void
sv_PrintObjectID(FILE *out, SECItem *oid, char *m)
{
    const char *name;
    SECOidData *oiddata;

    oiddata = SECOID_FindOID(oid);
    if (oiddata == NULL) {
        sv_PrintAsHex(out, oid, m);
        return;
    }
    name = oiddata->desc;

    if (m != NULL)
        fprintf(out, "%s", m);
    fprintf(out, "%s\n", name);
}

void
sv_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m)
{
    sv_PrintObjectID(out, &a->algorithm, m);

    if ((a->parameters.len != 2) ||
        (PORT_Memcmp(a->parameters.data, "\005\000", 2) != 0)) {
        /* Print args to algorithm */
        sv_PrintAsHex(out, &a->parameters, "Args=");
    }
}

void
sv_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m)
{
    SECItem *value;
    int i;
    char om[100];

    fprintf(out, "%s", m);

    /*
     * XXX Make this smarter; look at the type field and then decode
     * and print the value(s) appropriately!
     */
    sv_PrintObjectID(out, &(attr->type), "type=");
    if (attr->values != NULL) {
        i = 0;
        while ((value = attr->values[i]) != NULL) {
            snprintf(om, sizeof(om), "%svalue[%d]=%s", m, i++, attr->encoded ? "(encoded)" : "");
            if (attr->encoded || attr->typeTag == NULL) {
                sv_PrintAsHex(out, value, om);
            } else {
                switch (attr->typeTag->offset) {
                    default:
                        sv_PrintAsHex(out, value, om);
                        break;
                    case SEC_OID_PKCS9_CONTENT_TYPE:
                        sv_PrintObjectID(out, value, om);
                        break;
                    case SEC_OID_PKCS9_SIGNING_TIME:
                        sv_PrintTime(out, value, om);
                        break;
                }
            }
        }
    }
}

void
sv_PrintName(FILE *out, CERTName *name, char *msg)
{
    char *str;

    str = CERT_NameToAscii(name);
    fprintf(out, "%s%s\n", msg, str);
    PORT_Free(str);
}

#if 0
/*
** secu_PrintPKCS7EncContent
**   Prints a SEC_PKCS7EncryptedContentInfo (without decrypting it)
*/
void
secu_PrintPKCS7EncContent(FILE *out, SEC_PKCS7EncryptedContentInfo *src,
              char *m, int level)
{
    if (src->contentTypeTag == NULL)
    src->contentTypeTag = SECOID_FindOID(&(src->contentType));

    secu_Indent(out, level);
    fprintf(out, "%s:\n", m);
    secu_Indent(out, level + 1);
    fprintf(out, "Content Type: %s\n",
        (src->contentTypeTag != NULL) ? src->contentTypeTag->desc
                      : "Unknown");
    sv_PrintAlgorithmID(out, &(src->contentEncAlg),
              "Content Encryption Algorithm");
    sv_PrintAsHex(out, &(src->encContent),
            "Encrypted Content", level+1);
}

/*
** secu_PrintRecipientInfo
**   Prints a PKCS7RecipientInfo type
*/
void
secu_PrintRecipientInfo(FILE *out, SEC_PKCS7RecipientInfo *info, char *m,
            int level)
{
    secu_Indent(out, level); fprintf(out, "%s:\n", m);
    sv_PrintInteger(out, &(info->version), "Version");

    sv_PrintName(out, &(info->issuerAndSN->issuer), "Issuer");
    sv_PrintInteger(out, &(info->issuerAndSN->serialNumber),
              "Serial Number");

    /* Parse and display encrypted key */
    sv_PrintAlgorithmID(out, &(info->keyEncAlg),
            "Key Encryption Algorithm");
    sv_PrintAsHex(out, &(info->encKey), "Encrypted Key", level + 1);
}
#endif

/*
** secu_PrintSignerInfo
**   Prints a PKCS7SingerInfo type
*/
void
sv_PrintSignerInfo(FILE *out, SEC_PKCS7SignerInfo *info, char *m)
{
    SEC_PKCS7Attribute *attr;
    int iv;

    fprintf(out, "%s", m);
    sv_PrintInteger(out, &(info->version), "version=");

    fprintf(out, "%s", m);
    sv_PrintName(out, &(info->issuerAndSN->issuer), "issuerName=");
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &(info->issuerAndSN->serialNumber),
                    "serialNumber=");

    fprintf(out, "%s", m);
    sv_PrintAlgorithmID(out, &(info->digestAlg), "digestAlgorithm=");

    if (info->authAttr != NULL) {
        char mm[120];

        iv = 0;
        while (info->authAttr[iv] != NULL)
            iv++;
        fprintf(out, "%sauthenticatedAttributes=%d\n", m, iv);
        iv = 0;
        while ((attr = info->authAttr[iv]) != NULL) {
            snprintf(mm, sizeof(mm), "%sattribute[%d].", m, iv++);
            sv_PrintAttribute(out, attr, mm);
        }
    }

    /* Parse and display signature */
    fprintf(out, "%s", m);
    sv_PrintAlgorithmID(out, &(info->digestEncAlg), "digestEncryptionAlgorithm=");
    fprintf(out, "%s", m);
    sv_PrintAsHex(out, &(info->encDigest), "encryptedDigest=");

    if (info->unAuthAttr != NULL) {
        char mm[120];

        iv = 0;
        while (info->unAuthAttr[iv] != NULL)
            iv++;
        fprintf(out, "%sunauthenticatedAttributes=%d\n", m, iv);
        iv = 0;
        while ((attr = info->unAuthAttr[iv]) != NULL) {
            snprintf(mm, sizeof(mm), "%sattribute[%d].", m, iv++);
            sv_PrintAttribute(out, attr, mm);
        }
    }
}

void
sv_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m)
{
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.rsa.modulus, "modulus=");
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.rsa.publicExponent, "exponent=");
}

void
sv_PrintDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m)
{
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.dsa.params.prime, "prime=");
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.dsa.params.subPrime, "subprime=");
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.dsa.params.base, "base=");
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.dsa.publicValue, "publicValue=");
}

void
sv_PrintECDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m)
{
    SECItem curve = { siBuffer, NULL, 0 };
    if ((pk->u.ec.DEREncodedParams.len > 2) &&
        (pk->u.ec.DEREncodedParams.data[0] == 0x06)) {
        /* strip to just the oid for the curve */
        curve.len = pk->u.ec.DEREncodedParams.data[1];
        curve.data = pk->u.ec.DEREncodedParams.data + 2;
        /* don't overflow the buffer */
        curve.len = PR_MIN(curve.len, pk->u.ec.DEREncodedParams.len - 2);
        fprintf(out, "%s", m);
        sv_PrintObjectID(out, &curve, "curve=");
    }
    fprintf(out, "%s", m);
    sv_PrintInteger(out, &pk->u.ec.publicValue, "publicValue=");
}

int
sv_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena,
                             CERTSubjectPublicKeyInfo *i, char *msg)
{
    SECKEYPublicKey pk;
    int rv;
    char mm[200];

    snprintf(mm, sizeof(mm), "%s.publicKeyAlgorithm=", msg);
    sv_PrintAlgorithmID(out, &i->algorithm, mm);

    DER_ConvertBitString(&i->subjectPublicKey);
    switch (SECOID_FindOIDTag(&i->algorithm.algorithm)) {
        case SEC_OID_PKCS1_RSA_ENCRYPTION:
        case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
            rv = SEC_ASN1DecodeItem(arena, &pk,
                                    SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate),
                                    &i->subjectPublicKey);
            if (rv)
                return rv;
            snprintf(mm, sizeof(mm), "%s.rsaPublicKey.", msg);
            sv_PrintRSAPublicKey(out, &pk, mm);
            break;
        case SEC_OID_ANSIX9_DSA_SIGNATURE:
            rv = SEC_ASN1DecodeItem(arena, &pk,
                                    SEC_ASN1_GET(SECKEY_DSAPublicKeyTemplate),
                                    &i->subjectPublicKey);
            if (rv)
                return rv;
#ifdef notdef
            /* SECKEY_PQGParamsTemplate is not yet exported form NSS */
            rv = SEC_ASN1DecodeItem(arena, &pk.u.dsa.params,
                                    SEC_ASN1_GET(SECKEY_PQGParamsTemplate),
                                    &i->algorithm.parameters);
            if (rv)
                return rv;
#endif
            snprintf(mm, sizeof(mm), "%s.dsaPublicKey.", msg);
            sv_PrintDSAPublicKey(out, &pk, mm);
            break;
        case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
            rv = SECITEM_CopyItem(arena, &pk.u.ec.DEREncodedParams,
                                  &i->algorithm.parameters);
            if (rv)
                return rv;
            rv = SECITEM_CopyItem(arena, &pk.u.ec.publicValue,
                                  &i->subjectPublicKey);
            if (rv)
                return rv;
            snprintf(mm, sizeof(mm), "%s.ecdsaPublicKey.", msg);
            sv_PrintECDSAPublicKey(out, &pk, mm);
            break;
        default:
            fprintf(out, "%s=bad SPKI algorithm type\n", msg);
            return 0;
    }

    return 0;
}

SECStatus
sv_PrintInvalidDateExten(FILE *out, SECItem *value, char *msg)
{
    SECItem decodedValue;
    SECStatus rv;
    PRTime invalidTime;
    char *formattedTime = NULL;

    decodedValue.data = NULL;
    rv = SEC_ASN1DecodeItem(NULL, &decodedValue,
                            SEC_ASN1_GET(SEC_GeneralizedTimeTemplate),
                            value);
    if (rv == SECSuccess) {
        rv = DER_GeneralizedTimeToTime(&invalidTime, &decodedValue);
        if (rv == SECSuccess) {
            formattedTime = CERT_GenTime2FormattedAscii(invalidTime, "%a %b %d %H:%M:%S %Y");
            fprintf(out, "%s: %s\n", msg, formattedTime);
            PORT_Free(formattedTime);
        }
    }
    PORT_Free(decodedValue.data);

    return (rv);
}

int
sv_PrintExtensions(FILE *out, CERTCertExtension **extensions, char *msg)
{
    SECOidTag oidTag;

    if (extensions) {

        while (*extensions) {
            SECItem *tmpitem;

            fprintf(out, "%sname=", msg);

            tmpitem = &(*extensions)->id;
            sv_PrintObjectID(out, tmpitem, NULL);

            tmpitem = &(*extensions)->critical;
            if (tmpitem->len)
                fprintf(out, "%scritical=%s\n", msg,
                        (tmpitem->data && tmpitem->data[0]) ? "True" : "False");

            oidTag = SECOID_FindOIDTag(&((*extensions)->id));

            fprintf(out, "%s", msg);
            tmpitem = &((*extensions)->value);
            if (oidTag == SEC_OID_X509_INVALID_DATE)
                sv_PrintInvalidDateExten(out, tmpitem, "invalidExt");
            else
                sv_PrintAsHex(out, tmpitem, "data=");

            /*fprintf(out, "\n");*/
            extensions++;
        }
    }

    return 0;
}

/* callers of this function must make sure that the CERTSignedCrl
   from which they are extracting the CERTCrl has been fully-decoded.
   Otherwise it will not have the entries even though the CRL may have
   some */
void
sv_PrintCRLInfo(FILE *out, CERTCrl *crl, char *m)
{
    CERTCrlEntry *entry;
    int iv;
    char om[100];

    fprintf(out, "%s", m);
    sv_PrintAlgorithmID(out, &(crl->signatureAlg), "signatureAlgorithm=");
    fprintf(out, "%s", m);
    sv_PrintName(out, &(crl->name), "name=");
    fprintf(out, "%s", m);
    sv_PrintTime(out, &(crl->lastUpdate), "lastUpdate=");
    fprintf(out, "%s", m);
    sv_PrintTime(out, &(crl->nextUpdate), "nextUpdate=");

    if (crl->entries != NULL) {
        iv = 0;
        while ((entry = crl->entries[iv]) != NULL) {
            fprintf(out, "%sentry[%d].", m, iv);
            sv_PrintInteger(out, &(entry->serialNumber), "serialNumber=");
            fprintf(out, "%sentry[%d].", m, iv);
            sv_PrintTime(out, &(entry->revocationDate), "revocationDate=");
            snprintf(om, sizeof(om), "%sentry[%d].signedCRLEntriesExtensions.", m, iv++);
            sv_PrintExtensions(out, entry->extensions, om);
        }
    }
    snprintf(om, sizeof(om), "%ssignedCRLEntriesExtensions.", m);
    sv_PrintExtensions(out, crl->extensions, om);
}

int
sv_PrintCertificate(FILE *out, const SECItem *der, const char *m, int level)
{
    PLArenaPool *arena = NULL;
    CERTCertificate *c;
    int rv;
    int iv;
    char mm[200];

    /* Decode certificate */
    c = (CERTCertificate *)PORT_ZAlloc(sizeof(CERTCertificate));
    if (!c)
        return PORT_GetError();

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena)
        return SEC_ERROR_NO_MEMORY;

    rv = SEC_ASN1DecodeItem(arena, c, SEC_ASN1_GET(CERT_CertificateTemplate),
                            der);
    if (rv) {
        PORT_FreeArena(arena, PR_FALSE);
        return rv;
    }

    /* Pretty print it out */
    iv = DER_GetInteger(&c->version);
    fprintf(out, "%sversion=%d (0x%x)\n", m, iv + 1, iv);
    snprintf(mm, sizeof(mm), "%sserialNumber=", m);
    sv_PrintInteger(out, &c->serialNumber, mm);
    snprintf(mm, sizeof(mm), "%ssignatureAlgorithm=", m);
    sv_PrintAlgorithmID(out, &c->signature, mm);
    snprintf(mm, sizeof(mm), "%sissuerName=", m);
    sv_PrintName(out, &c->issuer, mm);
    snprintf(mm, sizeof(mm), "%svalidity.", m);
    sv_PrintValidity(out, &c->validity, mm);
    snprintf(mm, sizeof(mm), "%ssubject=", m);
    sv_PrintName(out, &c->subject, mm);
    snprintf(mm, sizeof(mm), "%ssubjectPublicKeyInfo", m);
    rv = sv_PrintSubjectPublicKeyInfo(out, arena, &c->subjectPublicKeyInfo, mm);
    if (rv) {
        PORT_FreeArena(arena, PR_FALSE);
        return rv;
    }
    snprintf(mm, sizeof(mm), "%ssignedExtensions.", m);
    sv_PrintExtensions(out, c->extensions, mm);

    PORT_FreeArena(arena, PR_FALSE);
    return 0;
}

int
sv_PrintSignedData(FILE *out, SECItem *der, char *m, SECU_PPFunc inner)
{
    PLArenaPool *arena = NULL;
    CERTSignedData *sd;
    int rv;

    /* Strip off the signature */
    sd = (CERTSignedData *)PORT_ZAlloc(sizeof(CERTSignedData));
    if (!sd)
        return PORT_GetError();

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena)
        return SEC_ERROR_NO_MEMORY;

    rv = SEC_ASN1DecodeItem(arena, sd, SEC_ASN1_GET(CERT_SignedDataTemplate),
                            der);
    if (rv) {
        PORT_FreeArena(arena, PR_FALSE);
        return rv;
    }

    /*    fprintf(out, "%s:\n", m); */
    PORT_Strcat(m, "data.");

    rv = (*inner)(out, &sd->data, m, 0);
    if (rv) {
        PORT_FreeArena(arena, PR_FALSE);
        return rv;
    }

    m[PORT_Strlen(m) - 5] = 0;
    fprintf(out, "%s", m);
    sv_PrintAlgorithmID(out, &sd->signatureAlgorithm, "signatureAlgorithm=");
    DER_ConvertBitString(&sd->signature);
    fprintf(out, "%s", m);
    sv_PrintAsHex(out, &sd->signature, "signature=");

    PORT_FreeArena(arena, PR_FALSE);
    return 0;
}

/*
** secu_PrintPKCS7Signed
**   Pretty print a PKCS7 signed data type (up to version 1).
*/
int
sv_PrintPKCS7Signed(FILE *out, SEC_PKCS7SignedData *src)
{
    SECAlgorithmID *digAlg;       /* digest algorithms */
    SECItem *aCert;               /* certificate */
    CERTSignedCrl *aCrl;          /* certificate revocation list */
    SEC_PKCS7SignerInfo *sigInfo; /* signer information */
    int rv, iv;
    char om[120];

    sv_PrintInteger(out, &(src->version), "pkcs7.version=");

    /* Parse and list digest algorithms (if any) */
    if (src->digestAlgorithms != NULL) {
        iv = 0;
        while (src->digestAlgorithms[iv] != NULL)
            iv++;
        fprintf(out, "pkcs7.digestAlgorithmListLength=%d\n", iv);
        iv = 0;
        while ((digAlg = src->digestAlgorithms[iv]) != NULL) {
            snprintf(om, sizeof(om), "pkcs7.digestAlgorithm[%d]=", iv++);
            sv_PrintAlgorithmID(out, digAlg, om);
        }
    }

    /* Now for the content */
    rv = sv_PrintPKCS7ContentInfo(out, &(src->contentInfo),
                                  "pkcs7.contentInformation=");
    if (rv != 0)
        return rv;

    /* Parse and list certificates (if any) */
    if (src->rawCerts != NULL) {
        iv = 0;
        while (src->rawCerts[iv] != NULL)
            iv++;
        fprintf(out, "pkcs7.certificateListLength=%d\n", iv);

        iv = 0;
        while ((aCert = src->rawCerts[iv]) != NULL) {
            snprintf(om, sizeof(om), "certificate[%d].", iv++);
            rv = sv_PrintSignedData(out, aCert, om, sv_PrintCertificate);
            if (rv)
                return rv;
        }
    }

    /* Parse and list CRL's (if any) */
    if (src->crls != NULL) {
        iv = 0;
        while (src->crls[iv] != NULL)
            iv++;
        fprintf(out, "pkcs7.signedRevocationLists=%d\n", iv);
        iv = 0;
        while ((aCrl = src->crls[iv]) != NULL) {
            snprintf(om, sizeof(om), "signedRevocationList[%d].", iv);
            fprintf(out, "%s", om);
            sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
                                "signatureAlgorithm=");
            DER_ConvertBitString(&aCrl->signatureWrap.signature);
            fprintf(out, "%s", om);
            sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "signature=");
            snprintf(om, sizeof(om), "certificateRevocationList[%d].", iv);
            sv_PrintCRLInfo(out, &aCrl->crl, om);
            iv++;
        }
    }

    /* Parse and list signatures (if any) */
    if (src->signerInfos != NULL) {
        iv = 0;
        while (src->signerInfos[iv] != NULL)
            iv++;
        fprintf(out, "pkcs7.signerInformationListLength=%d\n", iv);
        iv = 0;
        while ((sigInfo = src->signerInfos[iv]) != NULL) {
            snprintf(om, sizeof(om), "signerInformation[%d].", iv++);
            sv_PrintSignerInfo(out, sigInfo, om);
        }
    }

    return 0;
}

#if 0
/*
** secu_PrintPKCS7Enveloped
**  Pretty print a PKCS7 enveloped data type (up to version 1).
*/
void
secu_PrintPKCS7Enveloped(FILE *out, SEC_PKCS7EnvelopedData *src,
             char *m, int level)
{
    SEC_PKCS7RecipientInfo *recInfo;   /* pointer for signer information */
    int iv;
    char om[100];

    secu_Indent(out, level); fprintf(out, "%s:\n", m);
    sv_PrintInteger(out, &(src->version), "Version", level + 1);

    /* Parse and list recipients (this is not optional) */
    if (src->recipientInfos != NULL) {
    secu_Indent(out, level + 1);
    fprintf(out, "Recipient Information List:\n");
    iv = 0;
    while ((recInfo = src->recipientInfos[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Recipient Information (%x)", iv);
        secu_PrintRecipientInfo(out, recInfo, om, level + 2);
    }
    }

    secu_PrintPKCS7EncContent(out, &src->encContentInfo,
                  "Encrypted Content Information", level + 1);
}

/*
** secu_PrintPKCS7SignedEnveloped
**   Pretty print a PKCS7 singed and enveloped data type (up to version 1).
*/
int
secu_PrintPKCS7SignedAndEnveloped(FILE *out,
                  SEC_PKCS7SignedAndEnvelopedData *src,
                  char *m, int level)
{
    SECAlgorithmID *digAlg;  /* pointer for digest algorithms */
    SECItem *aCert;           /* pointer for certificate */
    CERTSignedCrl *aCrl;        /* pointer for certificate revocation list */
    SEC_PKCS7SignerInfo *sigInfo;   /* pointer for signer information */
    SEC_PKCS7RecipientInfo *recInfo; /* pointer for recipient information */
    int rv, iv;
    char om[100];

    secu_Indent(out, level); fprintf(out, "%s:\n", m);
    sv_PrintInteger(out, &(src->version), "Version", level + 1);

    /* Parse and list recipients (this is not optional) */
    if (src->recipientInfos != NULL) {
    secu_Indent(out, level + 1);
    fprintf(out, "Recipient Information List:\n");
    iv = 0;
    while ((recInfo = src->recipientInfos[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Recipient Information (%x)", iv);
        secu_PrintRecipientInfo(out, recInfo, om, level + 2);
    }
    }

    /* Parse and list digest algorithms (if any) */
    if (src->digestAlgorithms != NULL) {
    secu_Indent(out, level + 1);  fprintf(out, "Digest Algorithm List:\n");
    iv = 0;
    while ((digAlg = src->digestAlgorithms[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Digest Algorithm (%x)", iv);
        sv_PrintAlgorithmID(out, digAlg, om);
    }
    }

    secu_PrintPKCS7EncContent(out, &src->encContentInfo,
                  "Encrypted Content Information", level + 1);

    /* Parse and list certificates (if any) */
    if (src->rawCerts != NULL) {
    secu_Indent(out, level + 1);  fprintf(out, "Certificate List:\n");
    iv = 0;
    while ((aCert = src->rawCerts[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Certificate (%x)", iv);
        rv = SECU_PrintSignedData(out, aCert, om, level + 2,
                      SECU_PrintCertificate);
        if (rv)
        return rv;
    }
    }

    /* Parse and list CRL's (if any) */
    if (src->crls != NULL) {
    secu_Indent(out, level + 1);
    fprintf(out, "Signed Revocation Lists:\n");
    iv = 0;
    while ((aCrl = src->crls[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Signed Revocation List (%x)", iv);
        secu_Indent(out, level + 2);  fprintf(out, "%s:\n", om);
        sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
                  "Signature Algorithm");
        DER_ConvertBitString(&aCrl->signatureWrap.signature);
        sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "Signature",
                level+3);
        SECU_PrintCRLInfo(out, &aCrl->crl, "Certificate Revocation List",
              level + 3);
    }
    }

    /* Parse and list signatures (if any) */
    if (src->signerInfos != NULL) {
    secu_Indent(out, level + 1);
    fprintf(out, "Signer Information List:\n");
    iv = 0;
    while ((sigInfo = src->signerInfos[iv++]) != NULL) {
        snprintf(om, sizeof(om), "Signer Information (%x)", iv);
        secu_PrintSignerInfo(out, sigInfo, om, level + 2);
    }
    }

    return 0;
}

/*
** secu_PrintPKCS7Encrypted
**   Pretty print a PKCS7 encrypted data type (up to version 1).
*/
void
secu_PrintPKCS7Encrypted(FILE *out, SEC_PKCS7EncryptedData *src,
             char *m, int level)
{
    secu_Indent(out, level); fprintf(out, "%s:\n", m);
    sv_PrintInteger(out, &(src->version), "Version", level + 1);

    secu_PrintPKCS7EncContent(out, &src->encContentInfo,
                  "Encrypted Content Information", level + 1);
}

/*
** secu_PrintPKCS7Digested
**   Pretty print a PKCS7 digested data type (up to version 1).
*/
void
sv_PrintPKCS7Digested(FILE *out, SEC_PKCS7DigestedData *src)
{
    secu_Indent(out, level); fprintf(out, "%s:\n", m);
    sv_PrintInteger(out, &(src->version), "Version", level + 1);

    sv_PrintAlgorithmID(out, &src->digestAlg, "Digest Algorithm");
    sv_PrintPKCS7ContentInfo(out, &src->contentInfo, "Content Information",
                   level + 1);
    sv_PrintAsHex(out, &src->digest, "Digest", level + 1);
}

#endif

/*
** secu_PrintPKCS7ContentInfo
**   Takes a SEC_PKCS7ContentInfo type and sends the contents to the
** appropriate function
*/
int
sv_PrintPKCS7ContentInfo(FILE *out, SEC_PKCS7ContentInfo *src, char *m)
{
    const char *desc;
    SECOidTag kind;
    int rv;

    if (src->contentTypeTag == NULL)
        src->contentTypeTag = SECOID_FindOID(&(src->contentType));

    if (src->contentTypeTag == NULL) {
        desc = "Unknown";
        kind = SEC_OID_PKCS7_DATA;
    } else {
        desc = src->contentTypeTag->desc;
        kind = src->contentTypeTag->offset;
    }

    fprintf(out, "%s%s\n", m, desc);

    if (src->content.data == NULL) {
        fprintf(out, "pkcs7.data=<no content>\n");
        return 0;
    }

    rv = 0;
    switch (kind) {
        case SEC_OID_PKCS7_SIGNED_DATA: /* Signed Data */
            rv = sv_PrintPKCS7Signed(out, src->content.signedData);
            break;

        case SEC_OID_PKCS7_ENVELOPED_DATA: /* Enveloped Data */
            fprintf(out, "pkcs7EnvelopedData=<unsupported>\n");
            /*sv_PrintPKCS7Enveloped(out, src->content.envelopedData);*/
            break;

        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: /* Signed and Enveloped */
            fprintf(out, "pkcs7SignedEnvelopedData=<unsupported>\n");
            /*rv = sv_PrintPKCS7SignedAndEnveloped(out,
                                src->content.signedAndEnvelopedData);*/
            break;

        case SEC_OID_PKCS7_DIGESTED_DATA: /* Digested Data */
            fprintf(out, "pkcs7DigestedData=<unsupported>\n");
            /*sv_PrintPKCS7Digested(out, src->content.digestedData);*/
            break;

        case SEC_OID_PKCS7_ENCRYPTED_DATA: /* Encrypted Data */
            fprintf(out, "pkcs7EncryptedData=<unsupported>\n");
            /*sv_PrintPKCS7Encrypted(out, src->content.encryptedData);*/
            break;

        default:
            fprintf(out, "pkcs7UnknownData=<unsupported>\n");
            /*sv_PrintAsHex(out, src->content.data);*/
            break;
    }

    return rv;
}

int
SV_PrintPKCS7ContentInfo(FILE *out, SECItem *der)
{
    SEC_PKCS7ContentInfo *cinfo;
    int rv = -1;

    cinfo = SEC_PKCS7DecodeItem(der, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

    if (cinfo != NULL) {
        rv = sv_PrintPKCS7ContentInfo(out, cinfo, "pkcs7.contentInfo=");
        SEC_PKCS7DestroyContentInfo(cinfo);
    }

    return rv;
}
/*
** End of PKCS7 functions
*/
