Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/pkcs7/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 42 kB image not shown  

Quelle  p7create.c   Sprache: C

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


/*
 * PKCS7 creation.
 */


#include "p7local.h"

#include "cert.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
#include "secder.h"
#include "secpkcs5.h"

const int NSS_PBE_DEFAULT_ITERATION_COUNT = /* used in p12e.c too */
#ifdef DEBUG
    10000
#else
    600000
#endif
    ;

static SECStatus
sec_pkcs7_init_content_info(SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp,
                            SECOidTag kind, PRBool detached)
{
    void *thing;
    int version;
    SECItem *versionp;
    SECStatus rv;

    PORT_Assert(cinfo != NULL && poolp != NULL);
    if (cinfo == NULL || poolp == NULL)
        return SECFailure;

    cinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
    PORT_Assert(cinfo->contentTypeTag && cinfo->contentTypeTag->offset == kind);

    rv = SECITEM_CopyItem(poolp, &(cinfo->contentType),
                          &(cinfo->contentTypeTag->oid));
    if (rv != SECSuccess)
        return rv;

    if (detached)
        return SECSuccess;

    switch (kind) {
        default:
        case SEC_OID_PKCS7_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SECItem));
            cinfo->content.data = (SECItem *)thing;
            versionp = NULL;
            version = -1;
            break;
        case SEC_OID_PKCS7_DIGESTED_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7DigestedData));
            cinfo->content.digestedData = (SEC_PKCS7DigestedData *)thing;
            versionp = &(cinfo->content.digestedData->version);
            version = SEC_PKCS7_DIGESTED_DATA_VERSION;
            break;
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EncryptedData));
            cinfo->content.encryptedData = (SEC_PKCS7EncryptedData *)thing;
            versionp = &(cinfo->content.encryptedData->version);
            version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
            break;
        case SEC_OID_PKCS7_ENVELOPED_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EnvelopedData));
            cinfo->content.envelopedData =
                (SEC_PKCS7EnvelopedData *)thing;
            versionp = &(cinfo->content.envelopedData->version);
            version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
            break;
        case SEC_OID_PKCS7_SIGNED_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedData));
            cinfo->content.signedData =
                (SEC_PKCS7SignedData *)thing;
            versionp = &(cinfo->content.signedData->version);
            version = SEC_PKCS7_SIGNED_DATA_VERSION;
            break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedAndEnvelopedData));
            cinfo->content.signedAndEnvelopedData =
                (SEC_PKCS7SignedAndEnvelopedData *)thing;
            versionp = &(cinfo->content.signedAndEnvelopedData->version);
            version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
            break;
    }

    if (thing == NULL)
        return SECFailure;

    if (versionp != NULL) {
        SECItem *dummy;

        PORT_Assert(version >= 0);
        dummy = SEC_ASN1EncodeInteger(poolp, versionp, version);
        if (dummy == NULL)
            return SECFailure;
        PORT_Assert(dummy == versionp);
    }

    return SECSuccess;
}

static SEC_PKCS7ContentInfo *
sec_pkcs7_create_content_info(SECOidTag kind, PRBool detached,
                              SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    PLArenaPool *poolp;
    SECStatus rv;

    poolp = PORT_NewArena(1024); /* XXX what is right value? */
    if (poolp == NULL)
        return NULL;

    cinfo = (SEC_PKCS7ContentInfo *)PORT_ArenaZAlloc(poolp, sizeof(*cinfo));
    if (cinfo == NULL) {
        PORT_FreeArena(poolp, PR_FALSE);
        return NULL;
    }

    cinfo->poolp = poolp;
    cinfo->pwfn = pwfn;
    cinfo->pwfn_arg = pwfn_arg;
    cinfo->created = PR_TRUE;
    cinfo->refCount = 1;

    rv = sec_pkcs7_init_content_info(cinfo, poolp, kind, detached);
    if (rv != SECSuccess) {
        PORT_FreeArena(poolp, PR_FALSE);
        return NULL;
    }

    return cinfo;
}

/*
 * Add a signer to a PKCS7 thing, verifying the signature cert first.
 * Any error returns SECFailure.
 *
 * XXX Right now this only adds the *first* signer.  It fails if you try
 * to add a second one -- this needs to be fixed.
 */

static SECStatus
sec_pkcs7_add_signer(SEC_PKCS7ContentInfo *cinfo,
                     CERTCertificate *cert,
                     SECCertUsage certusage,
                     CERTCertDBHandle *certdb,
                     SECOidTag digestalgtag,
                     SECItem *digestdata)
{
    SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
    SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
    SECItem *digest, **digests, ***digestsp;
    SECItem *dummy;
    void *mark;
    SECStatus rv;
    SECOidTag kind;

    kind = SEC_PKCS7ContentType(cinfo);
    switch (kind) {
        case SEC_OID_PKCS7_SIGNED_DATA: {
            SEC_PKCS7SignedData *sdp;

            sdp = cinfo->content.signedData;
            digestalgsp = &(sdp->digestAlgorithms);
            digestsp = &(sdp->digests);
            signerinfosp = &(sdp->signerInfos);
        } break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
            SEC_PKCS7SignedAndEnvelopedData *saedp;

            saedp = cinfo->content.signedAndEnvelopedData;
            digestalgsp = &(saedp->digestAlgorithms);
            digestsp = &(saedp->digests);
            signerinfosp = &(saedp->signerInfos);
        } break;
        default:
            return SECFailure; /* XXX set an error? */
    }

    /*
     * XXX I think that CERT_VerifyCert should do this if *it* is passed
     * a NULL database.
     */

    if (certdb == NULL) {
        certdb = CERT_GetDefaultCertDB();
        if (certdb == NULL)
            return SECFailure; /* XXX set an error? */
    }

    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
                        cinfo->pwfn_arg, NULL) != SECSuccess) {
        /* XXX Did CERT_VerifyCert set an error? */
        return SECFailure;
    }

    /*
     * XXX This is the check that we do not already have a signer.
     * This is not what we really want -- we want to allow this
     * and *add* the new signer.
     */

    PORT_Assert(*signerinfosp == NULL && *digestalgsp == NULL && *digestsp == NULL);
    if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
        return SECFailure;

    mark = PORT_ArenaMark(cinfo->poolp);

    signerinfo = (SEC_PKCS7SignerInfo *)PORT_ArenaZAlloc(cinfo->poolp,
                                                         sizeof(SEC_PKCS7SignerInfo));
    if (signerinfo == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &signerinfo->version,
                                  SEC_PKCS7_SIGNER_INFO_VERSION);
    if (dummy == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }
    PORT_Assert(dummy == &signerinfo->version);

    signerinfo->cert = CERT_DupCertificate(cert);
    if (signerinfo->cert == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
    if (signerinfo->issuerAndSN == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    rv = SECOID_SetAlgorithmID(cinfo->poolp, &signerinfo->digestAlg,
                               digestalgtag, NULL);
    if (rv != SECSuccess) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    /*
     * Okay, now signerinfo is all set.  We just need to put it and its
     * companions (another copy of the digest algorithm, and the digest
     * itself if given) into the main structure.
     *
     * XXX If we are handling more than one signer, the following code
     * needs to look through the digest algorithms already specified
     * and see if the same one is there already.  If it is, it does not
     * need to be added again.  Also, if it is there *and* the digest
     * is not null, then the digest given should match the digest already
     * specified -- if not, that is an error.  Finally, the new signerinfo
     * should be *added* to the set already found.
     */


    signerinfos = (SEC_PKCS7SignerInfo **)PORT_ArenaAlloc(cinfo->poolp,
                                                          2 * sizeof(SEC_PKCS7SignerInfo *));
    if (signerinfos == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }
    signerinfos[0] = signerinfo;
    signerinfos[1] = NULL;

    digestalg = PORT_ArenaZAlloc(cinfo->poolp, sizeof(SECAlgorithmID));
    digestalgs = PORT_ArenaAlloc(cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
    if (digestalg == NULL || digestalgs == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }
    rv = SECOID_SetAlgorithmID(cinfo->poolp, digestalg, digestalgtag, NULL);
    if (rv != SECSuccess) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }
    digestalgs[0] = digestalg;
    digestalgs[1] = NULL;

    if (digestdata != NULL) {
        digest = (SECItem *)PORT_ArenaAlloc(cinfo->poolp, sizeof(SECItem));
        digests = (SECItem **)PORT_ArenaAlloc(cinfo->poolp,
                                              2 * sizeof(SECItem *));
        if (digest == NULL || digests == NULL) {
            PORT_ArenaRelease(cinfo->poolp, mark);
            return SECFailure;
        }
        rv = SECITEM_CopyItem(cinfo->poolp, digest, digestdata);
        if (rv != SECSuccess) {
            PORT_ArenaRelease(cinfo->poolp, mark);
            return SECFailure;
        }
        digests[0] = digest;
        digests[1] = NULL;
    } else {
        digests = NULL;
    }

    *signerinfosp = signerinfos;
    *digestalgsp = digestalgs;
    *digestsp = digests;

    PORT_ArenaUnmark(cinfo->poolp, mark);
    return SECSuccess;
}

/*
 * Helper function for creating an empty signedData.
 */

static SEC_PKCS7ContentInfo *
sec_pkcs7_create_signed_data(SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    SEC_PKCS7SignedData *sigd;
    SECStatus rv;

    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
                                          pwfn, pwfn_arg);
    if (cinfo == NULL)
        return NULL;

    sigd = cinfo->content.signedData;
    PORT_Assert(sigd != NULL);

    /*
     * XXX Might we want to allow content types other than data?
     * If so, via what interface?
     */

    rv = sec_pkcs7_init_content_info(&(sigd->contentInfo), cinfo->poolp,
                                     SEC_OID_PKCS7_DATA, PR_TRUE);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    return cinfo;
}

/*
 * Start a PKCS7 signing context.
 *
 * "cert" is the cert that will be used to sign the data.  It will be
 * checked for validity.
 *
 * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
 * XXX Maybe SECCertUsage should be split so that our caller just says
 * "email" and *we* add the "signing" part -- otherwise our caller
 * could be lying about the usage; we do not want to allow encryption
 * certs for signing or vice versa.
 *
 * "certdb" is the cert database to use for verifying the cert.
 * It can be NULL if a default database is available (like in the client).
 *
 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
 *
 * "digest" is the actual digest of the data.  It must be provided in
 * the case of detached data or NULL if the content will be included.
 *
 * The return value can be passed to functions which add things to
 * it like attributes, then eventually to SEC_PKCS7Encode() or to
 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
 * SEC_PKCS7DestroyContentInfo().
 *
 * An error results in a return value of NULL and an error set.
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
 */

SEC_PKCS7ContentInfo *
SEC_PKCS7CreateSignedData(CERTCertificate *cert,
                          SECCertUsage certusage,
                          CERTCertDBHandle *certdb,
                          SECOidTag digestalg,
                          SECItem *digest,
                          SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    SECStatus rv;

    cinfo = sec_pkcs7_create_signed_data(pwfn, pwfn_arg);
    if (cinfo == NULL)
        return NULL;

    rv = sec_pkcs7_add_signer(cinfo, cert, certusage, certdb,
                              digestalg, digest);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    return cinfo;
}

static SEC_PKCS7Attribute *
sec_pkcs7_create_attribute(PLArenaPool *poolp, SECOidTag oidtag,
                           SECItem *value, PRBool encoded)
{
    SEC_PKCS7Attribute *attr;
    SECItem **values;
    void *mark;

    PORT_Assert(poolp != NULL);
    mark = PORT_ArenaMark(poolp);

    attr = (SEC_PKCS7Attribute *)PORT_ArenaAlloc(poolp,
                                                 sizeof(SEC_PKCS7Attribute));
    if (attr == NULL)
        goto loser;

    attr->typeTag = SECOID_FindOIDByTag(oidtag);
    if (attr->typeTag == NULL)
        goto loser;

    if (SECITEM_CopyItem(poolp, &(attr->type),
                         &(attr->typeTag->oid)) != SECSuccess)
        goto loser;

    values = (SECItem **)PORT_ArenaAlloc(poolp, 2 * sizeof(SECItem *));
    if (values == NULL)
        goto loser;

    if (value != NULL) {
        SECItem *copy;

        copy = (SECItem *)PORT_ArenaAlloc(poolp, sizeof(SECItem));
        if (copy == NULL)
            goto loser;

        if (SECITEM_CopyItem(poolp, copy, value) != SECSuccess)
            goto loser;

        value = copy;
    }

    values[0] = value;
    values[1] = NULL;
    attr->values = values;
    attr->encoded = encoded;

    PORT_ArenaUnmark(poolp, mark);
    return attr;

loser:
    PORT_Assert(mark != NULL);
    PORT_ArenaRelease(poolp, mark);
    return NULL;
}

static SECStatus
sec_pkcs7_add_attribute(SEC_PKCS7ContentInfo *cinfo,
                        SEC_PKCS7Attribute ***attrsp,
                        SEC_PKCS7Attribute *attr)
{
    SEC_PKCS7Attribute **attrs;
    SECItem *ct_value;
    void *mark;

    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
        return SECFailure;

    attrs = *attrsp;
    if (attrs != NULL) {
        int count;

        /*
         * We already have some attributes, and just need to add this
         * new one.
         */


        /*
         * We should already have the *required* attributes, which were
         * created/added at the same time the first attribute was added.
         */

        PORT_Assert(sec_PKCS7FindAttribute(attrs,
                                           SEC_OID_PKCS9_CONTENT_TYPE,
                                           PR_FALSE) != NULL);
        PORT_Assert(sec_PKCS7FindAttribute(attrs,
                                           SEC_OID_PKCS9_MESSAGE_DIGEST,
                                           PR_FALSE) != NULL);

        for (count = 0; attrs[count] != NULL; count++)
            ;
        attrs = (SEC_PKCS7Attribute **)PORT_ArenaGrow(cinfo->poolp, attrs,
                                                      (count + 1) * sizeof(SEC_PKCS7Attribute *),
                                                      (count + 2) * sizeof(SEC_PKCS7Attribute *));
        if (attrs == NULL)
            return SECFailure;

        attrs[count] = attr;
        attrs[count + 1] = NULL;
        *attrsp = attrs;

        return SECSuccess;
    }

    /*
     * This is the first time an attribute is going in.
     * We need to create and add the required attributes, and then
     * we will also add in the one our caller gave us.
     */


    /*
     * There are 2 required attributes, plus the one our caller wants
     * to add, plus we always end with a NULL one.  Thus, four slots.
     */

    attrs = (SEC_PKCS7Attribute **)PORT_ArenaAlloc(cinfo->poolp,
                                                   4 * sizeof(SEC_PKCS7Attribute *));
    if (attrs == NULL)
        return SECFailure;

    mark = PORT_ArenaMark(cinfo->poolp);

    /*
     * First required attribute is the content type of the data
     * being signed.
     */

    ct_value = &(cinfo->content.signedData->contentInfo.contentType);
    attrs[0] = sec_pkcs7_create_attribute(cinfo->poolp,
                                          SEC_OID_PKCS9_CONTENT_TYPE,
                                          ct_value, PR_FALSE);
    /*
     * Second required attribute is the message digest of the data
     * being signed; we leave the value NULL for now (just create
     * the place for it to go), and the encoder will fill it in later.
     */

    attrs[1] = sec_pkcs7_create_attribute(cinfo->poolp,
                                          SEC_OID_PKCS9_MESSAGE_DIGEST,
                                          NULL, PR_FALSE);
    if (attrs[0] == NULL || attrs[1] == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    attrs[2] = attr;
    attrs[3] = NULL;
    *attrsp = attrs;

    PORT_ArenaUnmark(cinfo->poolp, mark);
    return SECSuccess;
}

/*
 * Add the signing time to the authenticated (i.e. signed) attributes
 * of "cinfo".  This is expected to be included in outgoing signed
 * messages for email (S/MIME) but is likely useful in other situations.
 *
 * This should only be added once; a second call will either do
 * nothing or replace an old signing time with a newer one.
 *
 * XXX This will probably just shove the current time into "cinfo"
 * but it will not actually get signed until the entire item is
 * processed for encoding.  Is this (expected to be small) delay okay?
 *
 * "cinfo" should be of type signedData (the only kind of pkcs7 data
 * that is allowed authenticated attributes); SECFailure will be returned
 * if it is not.
 */

SECStatus
SEC_PKCS7AddSigningTime(SEC_PKCS7ContentInfo *cinfo)
{
    SEC_PKCS7SignerInfo **signerinfos;
    SEC_PKCS7Attribute *attr;
    SECItem stime;
    SECStatus rv;
    int si;

    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
        return SECFailure;

    signerinfos = cinfo->content.signedData->signerInfos;

    /* There has to be a signer, or it makes no sense. */
    if (signerinfos == NULL || signerinfos[0] == NULL)
        return SECFailure;

    rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
    if (rv != SECSuccess)
        return rv;

    attr = sec_pkcs7_create_attribute(cinfo->poolp,
                                      SEC_OID_PKCS9_SIGNING_TIME,
                                      &stime, PR_FALSE);
    SECITEM_FreeItem(&stime, PR_FALSE);

    if (attr == NULL)
        return SECFailure;

    rv = SECSuccess;
    for (si = 0; signerinfos[si] != NULL; si++) {
        SEC_PKCS7Attribute *oattr;

        oattr = sec_PKCS7FindAttribute(signerinfos[si]->authAttr,
                                       SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
        PORT_Assert(oattr == NULL);
        if (oattr != NULL)
            continue/* XXX or would it be better to replace it? */

        rv = sec_pkcs7_add_attribute(cinfo, &(signerinfos[si]->authAttr),
                                     attr);
        if (rv != SECSuccess)
            break/* could try to continue, but may as well give up now */
    }

    return rv;
}

/*
 * Add the specified attribute to the authenticated (i.e. signed) attributes
 * of "cinfo" -- "oidtag" describes the attribute and "value" is the
 * value to be associated with it.  NOTE! "value" must already be encoded;
 * no interpretation of "oidtag" is done.  Also, it is assumed that this
 * signedData has only one signer -- if we ever need to add attributes
 * when there is more than one signature, we need a way to specify *which*
 * signature should get the attribute.
 *
 * XXX Technically, a signed attribute can have multiple values; if/when
 * we ever need to support an attribute which takes multiple values, we
 * either need to change this interface or create an AddSignedAttributeValue
 * which can be called subsequently, and would then append a value.
 *
 * "cinfo" should be of type signedData (the only kind of pkcs7 data
 * that is allowed authenticated attributes); SECFailure will be returned
 * if it is not.
 */

SECStatus
SEC_PKCS7AddSignedAttribute(SEC_PKCS7ContentInfo *cinfo,
                            SECOidTag oidtag,
                            SECItem *value)
{
    SEC_PKCS7SignerInfo **signerinfos;
    SEC_PKCS7Attribute *attr;

    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
        return SECFailure;

    signerinfos = cinfo->content.signedData->signerInfos;

    /*
     * No signature or more than one means no deal.
     */

    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
        return SECFailure;

    attr = sec_pkcs7_create_attribute(cinfo->poolp, oidtag, value, PR_TRUE);
    if (attr == NULL)
        return SECFailure;

    return sec_pkcs7_add_attribute(cinfo, &(signerinfos[0]->authAttr), attr);
}

/*
 * Mark that the signer certificates and their issuing chain should
 * be included in the encoded data.  This is expected to be used
 * in outgoing signed messages for email (S/MIME).
 *
 * "certdb" is the cert database to use for finding the chain.
 * It can be NULL, meaning use the default database.
 *
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
 * SECFailure will be returned if it is not.
 */

SECStatus
SEC_PKCS7IncludeCertChain(SEC_PKCS7ContentInfo *cinfo,
                          CERTCertDBHandle *certdb)
{
    SECOidTag kind;
    SEC_PKCS7SignerInfo *signerinfo, **signerinfos;

    kind = SEC_PKCS7ContentType(cinfo);
    switch (kind) {
        case SEC_OID_PKCS7_SIGNED_DATA:
            signerinfos = cinfo->content.signedData->signerInfos;
            break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
            signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
            break;
        default:
            return SECFailure; /* XXX set an error? */
    }

    if (signerinfos == NULL) /* no signer, no certs? */
        return SECFailure;   /* XXX set an error? */

    if (certdb == NULL) {
        certdb = CERT_GetDefaultCertDB();
        if (certdb == NULL) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            return SECFailure;
        }
    }

    /* XXX Should it be an error if we find no signerinfo or no certs? */
    while ((signerinfo = *signerinfos++) != NULL) {
        if (signerinfo->cert != NULL)
            /* get the cert chain.  don't send the root to avoid contamination
             * of old clients with a new root that they don't trust
             */

            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
                                                          certUsageEmailSigner,
                                                          PR_FALSE);
    }

    return SECSuccess;
}

/*
 * Helper function to add a certificate chain for inclusion in the
 * bag of certificates in a signedData.
 */

static SECStatus
sec_pkcs7_add_cert_chain(SEC_PKCS7ContentInfo *cinfo,
                         CERTCertificate *cert,
                         CERTCertDBHandle *certdb)
{
    SECOidTag kind;
    CERTCertificateList *certlist, **certlists, ***certlistsp;
    int count;

    kind = SEC_PKCS7ContentType(cinfo);
    switch (kind) {
        case SEC_OID_PKCS7_SIGNED_DATA: {
            SEC_PKCS7SignedData *sdp;

            sdp = cinfo->content.signedData;
            certlistsp = &(sdp->certLists);
        } break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
            SEC_PKCS7SignedAndEnvelopedData *saedp;

            saedp = cinfo->content.signedAndEnvelopedData;
            certlistsp = &(saedp->certLists);
        } break;
        default:
            return SECFailure; /* XXX set an error? */
    }

    if (certdb == NULL) {
        certdb = CERT_GetDefaultCertDB();
        if (certdb == NULL) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            return SECFailure;
        }
    }

    certlist = CERT_CertChainFromCert(cert, certUsageEmailSigner, PR_FALSE);
    if (certlist == NULL)
        return SECFailure;

    certlists = *certlistsp;
    if (certlists == NULL) {
        count = 0;
        certlists = (CERTCertificateList **)PORT_ArenaAlloc(cinfo->poolp,
                                                            2 * sizeof(CERTCertificateList *));
    } else {
        for (count = 0; certlists[count] != NULL; count++)
            ;
        PORT_Assert(count); /* should be at least one already */
        certlists = (CERTCertificateList **)PORT_ArenaGrow(cinfo->poolp,
                                                           certlists,
                                                           (count + 1) * sizeof(CERTCertificateList *),
                                                           (count + 2) * sizeof(CERTCertificateList *));
    }

    if (certlists == NULL) {
        CERT_DestroyCertificateList(certlist);
        return SECFailure;
    }

    certlists[count] = certlist;
    certlists[count + 1] = NULL;

    *certlistsp = certlists;

    return SECSuccess;
}

/*
 * Helper function to add a certificate for inclusion in the bag of
 * certificates in a signedData.
 */

static SECStatus
sec_pkcs7_add_certificate(SEC_PKCS7ContentInfo *cinfo,
                          CERTCertificate *cert)
{
    SECOidTag kind;
    CERTCertificate **certs, ***certsp;
    int count;

    kind = SEC_PKCS7ContentType(cinfo);
    switch (kind) {
        case SEC_OID_PKCS7_SIGNED_DATA: {
            SEC_PKCS7SignedData *sdp;

            sdp = cinfo->content.signedData;
            certsp = &(sdp->certs);
        } break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
            SEC_PKCS7SignedAndEnvelopedData *saedp;

            saedp = cinfo->content.signedAndEnvelopedData;
            certsp = &(saedp->certs);
        } break;
        default:
            return SECFailure; /* XXX set an error? */
    }

    cert = CERT_DupCertificate(cert);
    if (cert == NULL)
        return SECFailure;

    certs = *certsp;
    if (certs == NULL) {
        count = 0;
        certs = (CERTCertificate **)PORT_ArenaAlloc(cinfo->poolp,
                                                    2 * sizeof(CERTCertificate *));
    } else {
        for (count = 0; certs[count] != NULL; count++)
            ;
        PORT_Assert(count); /* should be at least one already */
        certs = (CERTCertificate **)PORT_ArenaGrow(cinfo->poolp, certs,
                                                   (count + 1) * sizeof(CERTCertificate *),
                                                   (count + 2) * sizeof(CERTCertificate *));
    }

    if (certs == NULL) {
        CERT_DestroyCertificate(cert);
        return SECFailure;
    }

    certs[count] = cert;
    certs[count + 1] = NULL;

    *certsp = certs;

    return SECSuccess;
}

/*
 * Create a PKCS7 certs-only container.
 *
 * "cert" is the (first) cert that will be included.
 *
 * "include_chain" specifies whether the entire chain for "cert" should
 * be included.
 *
 * "certdb" is the cert database to use for finding the chain.
 * It can be NULL in when "include_chain" is false, or when meaning
 * use the default database.
 *
 * More certs and chains can be added via AddCertificate and AddCertChain.
 *
 * An error results in a return value of NULL and an error set.
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
 */

SEC_PKCS7ContentInfo *
SEC_PKCS7CreateCertsOnly(CERTCertificate *cert,
                         PRBool include_chain,
                         CERTCertDBHandle *certdb)
{
    SEC_PKCS7ContentInfo *cinfo;
    SECStatus rv;

    cinfo = sec_pkcs7_create_signed_data(NULL, NULL);
    if (cinfo == NULL)
        return NULL;

    if (include_chain)
        rv = sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
    else
        rv = sec_pkcs7_add_certificate(cinfo, cert);

    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    return cinfo;
}

/*
 * Add "cert" and its entire chain to the set of certs included in "cinfo".
 *
 * "certdb" is the cert database to use for finding the chain.
 * It can be NULL, meaning use the default database.
 *
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
 * SECFailure will be returned if it is not.
 */

SECStatus
SEC_PKCS7AddCertChain(SEC_PKCS7ContentInfo *cinfo,
                      CERTCertificate *cert,
                      CERTCertDBHandle *certdb)
{
    SECOidTag kind;

    kind = SEC_PKCS7ContentType(cinfo);
    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
        return SECFailure; /* XXX set an error? */

    return sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
}

/*
 * Add "cert" to the set of certs included in "cinfo".
 *
 * "cinfo" should be of type signedData or signedAndEnvelopedData;
 * SECFailure will be returned if it is not.
 */

SECStatus
SEC_PKCS7AddCertificate(SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
{
    SECOidTag kind;

    kind = SEC_PKCS7ContentType(cinfo);
    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
        return SECFailure; /* XXX set an error? */

    return sec_pkcs7_add_certificate(cinfo, cert);
}

static SECStatus
sec_pkcs7_init_encrypted_content_info(SEC_PKCS7EncryptedContentInfo *enccinfo,
                                      PLArenaPool *poolp,
                                      SECOidTag kind, PRBool detached,
                                      SECOidTag encalg, int keysize)
{
    SECStatus rv;

    PORT_Assert(enccinfo != NULL && poolp != NULL);
    if (enccinfo == NULL || poolp == NULL)
        return SECFailure;

    /*
     * XXX Some day we may want to allow for other kinds.  That needs
     * more work and modifications to the creation interface, etc.
     * For now, allow but notice callers who pass in other kinds.
     * They are responsible for creating the inner type and encoding,
     * if it is other than DATA.
     */

    PORT_Assert(kind == SEC_OID_PKCS7_DATA);

    enccinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
    PORT_Assert(enccinfo->contentTypeTag && enccinfo->contentTypeTag->offset == kind);

    rv = SECITEM_CopyItem(poolp, &(enccinfo->contentType),
                          &(enccinfo->contentTypeTag->oid));
    if (rv != SECSuccess)
        return rv;

    /* Save keysize and algorithm for later. */
    enccinfo->keysize = keysize;
    enccinfo->encalg = encalg;

    return SECSuccess;
}

/*
 * Add a recipient to a PKCS7 thing, verifying their cert first.
 * Any error returns SECFailure.
 */

static SECStatus
sec_pkcs7_add_recipient(SEC_PKCS7ContentInfo *cinfo,
                        CERTCertificate *cert,
                        SECCertUsage certusage,
                        CERTCertDBHandle *certdb)
{
    SECOidTag kind;
    SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
    SECItem *dummy;
    void *mark;
    int count;

    kind = SEC_PKCS7ContentType(cinfo);
    switch (kind) {
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
            SEC_PKCS7EnvelopedData *edp;

            edp = cinfo->content.envelopedData;
            recipientinfosp = &(edp->recipientInfos);
        } break;
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
            SEC_PKCS7SignedAndEnvelopedData *saedp;

            saedp = cinfo->content.signedAndEnvelopedData;
            recipientinfosp = &(saedp->recipientInfos);
        } break;
        default:
            return SECFailure; /* XXX set an error? */
    }

    /*
     * XXX I think that CERT_VerifyCert should do this if *it* is passed
     * a NULL database.
     */

    if (certdb == NULL) {
        certdb = CERT_GetDefaultCertDB();
        if (certdb == NULL)
            return SECFailure; /* XXX set an error? */
    }

    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
                        cinfo->pwfn_arg, NULL) != SECSuccess) {
        /* XXX Did CERT_VerifyCert set an error? */
        return SECFailure;
    }

    mark = PORT_ArenaMark(cinfo->poolp);

    recipientinfo = (SEC_PKCS7RecipientInfo *)PORT_ArenaZAlloc(cinfo->poolp,
                                                               sizeof(SEC_PKCS7RecipientInfo));
    if (recipientinfo == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &recipientinfo->version,
                                  SEC_PKCS7_RECIPIENT_INFO_VERSION);
    if (dummy == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }
    PORT_Assert(dummy == &recipientinfo->version);

    recipientinfo->cert = CERT_DupCertificate(cert);
    if (recipientinfo->cert == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
    if (recipientinfo->issuerAndSN == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    /*
     * Okay, now recipientinfo is all set.  We just need to put it into
     * the main structure.
     *
     * If this is the first recipient, allocate a new recipientinfos array;
     * otherwise, reallocate the array, making room for the new entry.
     */

    recipientinfos = *recipientinfosp;
    if (recipientinfos == NULL) {
        count = 0;
        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc(
            cinfo->poolp,
            2 * sizeof(SEC_PKCS7RecipientInfo *));
    } else {
        for (count = 0; recipientinfos[count] != NULL; count++)
            ;
        PORT_Assert(count); /* should be at least one already */
        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow(
            cinfo->poolp, recipientinfos,
            (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
            (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
    }

    if (recipientinfos == NULL) {
        PORT_ArenaRelease(cinfo->poolp, mark);
        return SECFailure;
    }

    recipientinfos[count] = recipientinfo;
    recipientinfos[count + 1] = NULL;

    *recipientinfosp = recipientinfos;

    PORT_ArenaUnmark(cinfo->poolp, mark);
    return SECSuccess;
}

/*
 * Start a PKCS7 enveloping context.
 *
 * "cert" is the cert for the recipient.  It will be checked for validity.
 *
 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
 * XXX Maybe SECCertUsage should be split so that our caller just says
 * "email" and *we* add the "recipient" part -- otherwise our caller
 * could be lying about the usage; we do not want to allow encryption
 * certs for signing or vice versa.
 *
 * "certdb" is the cert database to use for verifying the cert.
 * It can be NULL if a default database is available (like in the client).
 *
 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
 *
 * "keysize" specifies the bulk encryption key size, in bits.
 *
 * The return value can be passed to functions which add things to
 * it like more recipients, then eventually to SEC_PKCS7Encode() or to
 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
 * SEC_PKCS7DestroyContentInfo().
 *
 * An error results in a return value of NULL and an error set.
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
 */

extern SEC_PKCS7ContentInfo *
SEC_PKCS7CreateEnvelopedData(CERTCertificate *cert,
                             SECCertUsage certusage,
                             CERTCertDBHandle *certdb,
                             SECOidTag encalg,
                             int keysize,
                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    SEC_PKCS7EnvelopedData *envd;
    SECStatus rv;

    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENVELOPED_DATA,
                                          PR_FALSE, pwfn, pwfn_arg);
    if (cinfo == NULL)
        return NULL;

    rv = sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    envd = cinfo->content.envelopedData;
    PORT_Assert(envd != NULL);

    /*
     * XXX Might we want to allow content types other than data?
     * If so, via what interface?
     */

    rv = sec_pkcs7_init_encrypted_content_info(&(envd->encContentInfo),
                                               cinfo->poolp,
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
                                               encalg, keysize);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    /* XXX Anything more to do here? */

    return cinfo;
}

/*
 * Add another recipient to an encrypted message.
 *
 * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
 * SECFailure will be returned if it is not.
 *
 * "cert" is the cert for the recipient.  It will be checked for validity.
 *
 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
 * XXX Maybe SECCertUsage should be split so that our caller just says
 * "email" and *we* add the "recipient" part -- otherwise our caller
 * could be lying about the usage; we do not want to allow encryption
 * certs for signing or vice versa.
 *
 * "certdb" is the cert database to use for verifying the cert.
 * It can be NULL if a default database is available (like in the client).
 */

SECStatus
SEC_PKCS7AddRecipient(SEC_PKCS7ContentInfo *cinfo,
                      CERTCertificate *cert,
                      SECCertUsage certusage,
                      CERTCertDBHandle *certdb)
{
    return sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
}

/*
 * Create an empty PKCS7 data content info.
 *
 * An error results in a return value of NULL and an error set.
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
 */

SEC_PKCS7ContentInfo *
SEC_PKCS7CreateData(void)
{
    return sec_pkcs7_create_content_info(SEC_OID_PKCS7_DATA, PR_FALSE,
                                         NULL, NULL);
}

/*
 * Create an empty PKCS7 encrypted content info.
 *
 * "algorithm" specifies the bulk encryption algorithm to use.
 *
 * An error results in a return value of NULL and an error set.
 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
 */

SEC_PKCS7ContentInfo *
SEC_PKCS7CreateEncryptedData(SECOidTag algorithm, int keysize,
                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    SECAlgorithmID *algid;
    SEC_PKCS7EncryptedData *enc_data;
    SECStatus rv;

    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
                                          PR_FALSE, pwfn, pwfn_arg);
    if (cinfo == NULL)
        return NULL;

    enc_data = cinfo->content.encryptedData;
    algid = &(enc_data->encContentInfo.contentEncAlg);

    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
        rv = SECOID_SetAlgorithmID(cinfo->poolp, algid, algorithm, NULL);
    } else {
        /* Assume password-based-encryption.
         * Note: we can't generate pkcs5v2 from this interface.
         * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
         * non-PBE oids and assuming that they are pkcs5v2 oids, but
         * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
         * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
         * to create pkcs5v2 PBEs */

        SECAlgorithmID *pbe_algid;
        pbe_algid = PK11_CreatePBEAlgorithmID(algorithm,
                                              NSS_PBE_DEFAULT_ITERATION_COUNT,
                                              NULL);
        if (pbe_algid == NULL) {
            rv = SECFailure;
        } else {
            rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
            SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
        }
    }

    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
                                               cinfo->poolp,
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
                                               algorithm, keysize);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    return cinfo;
}

SEC_PKCS7ContentInfo *
SEC_PKCS7CreateEncryptedDataWithPBEV2(SECOidTag pbe_algorithm,
                                      SECOidTag cipher_algorithm,
                                      SECOidTag prf_algorithm,
                                      int keysize,
                                      SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
    SEC_PKCS7ContentInfo *cinfo;
    SECAlgorithmID *algid;
    SEC_PKCS7EncryptedData *enc_data;
    SECStatus rv;

    PORT_Assert(SEC_PKCS5IsAlgorithmPBEAlgTag(pbe_algorithm));

    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
                                          PR_FALSE, pwfn, pwfn_arg);
    if (cinfo == NULL)
        return NULL;

    enc_data = cinfo->content.encryptedData;
    algid = &(enc_data->encContentInfo.contentEncAlg);

    SECAlgorithmID *pbe_algid;
    pbe_algid = PK11_CreatePBEV2AlgorithmID(pbe_algorithm,
                                            cipher_algorithm,
                                            prf_algorithm,
                                            keysize,
                                            NSS_PBE_DEFAULT_ITERATION_COUNT,
                                            NULL);
    if (pbe_algid == NULL) {
        rv = SECFailure;
    } else {
        rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
        SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
    }

    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
                                               cinfo->poolp,
                                               SEC_OID_PKCS7_DATA, PR_FALSE,
                                               cipher_algorithm, keysize);
    if (rv != SECSuccess) {
        SEC_PKCS7DestroyContentInfo(cinfo);
        return NULL;
    }

    return cinfo;
}

Messung V0.5
C=97 H=88 G=92

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.