tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

p7create.c (43389B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * PKCS7 creation.
      7 */
      8 
      9 #include "p7local.h"
     10 
     11 #include "cert.h"
     12 #include "secasn1.h"
     13 #include "secitem.h"
     14 #include "secoid.h"
     15 #include "pk11func.h"
     16 #include "prtime.h"
     17 #include "secerr.h"
     18 #include "secder.h"
     19 #include "secpkcs5.h"
     20 
     21 const int NSS_PBE_DEFAULT_ITERATION_COUNT = /* used in p12e.c too */
     22 #ifdef DEBUG
     23    10000
     24 #else
     25    600000
     26 #endif
     27    ;
     28 
     29 static SECStatus
     30 sec_pkcs7_init_content_info(SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp,
     31                            SECOidTag kind, PRBool detached)
     32 {
     33    void *thing;
     34    int version;
     35    SECItem *versionp;
     36    SECStatus rv;
     37 
     38    PORT_Assert(cinfo != NULL && poolp != NULL);
     39    if (cinfo == NULL || poolp == NULL)
     40        return SECFailure;
     41 
     42    cinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
     43    PORT_Assert(cinfo->contentTypeTag && cinfo->contentTypeTag->offset == kind);
     44 
     45    rv = SECITEM_CopyItem(poolp, &(cinfo->contentType),
     46                          &(cinfo->contentTypeTag->oid));
     47    if (rv != SECSuccess)
     48        return rv;
     49 
     50    if (detached)
     51        return SECSuccess;
     52 
     53    switch (kind) {
     54        default:
     55        case SEC_OID_PKCS7_DATA:
     56            thing = PORT_ArenaZAlloc(poolp, sizeof(SECItem));
     57            cinfo->content.data = (SECItem *)thing;
     58            versionp = NULL;
     59            version = -1;
     60            break;
     61        case SEC_OID_PKCS7_DIGESTED_DATA:
     62            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7DigestedData));
     63            cinfo->content.digestedData = (SEC_PKCS7DigestedData *)thing;
     64            versionp = &(cinfo->content.digestedData->version);
     65            version = SEC_PKCS7_DIGESTED_DATA_VERSION;
     66            break;
     67        case SEC_OID_PKCS7_ENCRYPTED_DATA:
     68            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EncryptedData));
     69            cinfo->content.encryptedData = (SEC_PKCS7EncryptedData *)thing;
     70            versionp = &(cinfo->content.encryptedData->version);
     71            version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
     72            break;
     73        case SEC_OID_PKCS7_ENVELOPED_DATA:
     74            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7EnvelopedData));
     75            cinfo->content.envelopedData =
     76                (SEC_PKCS7EnvelopedData *)thing;
     77            versionp = &(cinfo->content.envelopedData->version);
     78            version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
     79            break;
     80        case SEC_OID_PKCS7_SIGNED_DATA:
     81            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedData));
     82            cinfo->content.signedData =
     83                (SEC_PKCS7SignedData *)thing;
     84            versionp = &(cinfo->content.signedData->version);
     85            version = SEC_PKCS7_SIGNED_DATA_VERSION;
     86            break;
     87        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
     88            thing = PORT_ArenaZAlloc(poolp, sizeof(SEC_PKCS7SignedAndEnvelopedData));
     89            cinfo->content.signedAndEnvelopedData =
     90                (SEC_PKCS7SignedAndEnvelopedData *)thing;
     91            versionp = &(cinfo->content.signedAndEnvelopedData->version);
     92            version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
     93            break;
     94    }
     95 
     96    if (thing == NULL)
     97        return SECFailure;
     98 
     99    if (versionp != NULL) {
    100        SECItem *dummy;
    101 
    102        PORT_Assert(version >= 0);
    103        dummy = SEC_ASN1EncodeInteger(poolp, versionp, version);
    104        if (dummy == NULL)
    105            return SECFailure;
    106        PORT_Assert(dummy == versionp);
    107    }
    108 
    109    return SECSuccess;
    110 }
    111 
    112 static SEC_PKCS7ContentInfo *
    113 sec_pkcs7_create_content_info(SECOidTag kind, PRBool detached,
    114                              SECKEYGetPasswordKey pwfn, void *pwfn_arg)
    115 {
    116    SEC_PKCS7ContentInfo *cinfo;
    117    PLArenaPool *poolp;
    118    SECStatus rv;
    119 
    120    poolp = PORT_NewArena(1024); /* XXX what is right value? */
    121    if (poolp == NULL)
    122        return NULL;
    123 
    124    cinfo = (SEC_PKCS7ContentInfo *)PORT_ArenaZAlloc(poolp, sizeof(*cinfo));
    125    if (cinfo == NULL) {
    126        PORT_FreeArena(poolp, PR_FALSE);
    127        return NULL;
    128    }
    129 
    130    cinfo->poolp = poolp;
    131    cinfo->pwfn = pwfn;
    132    cinfo->pwfn_arg = pwfn_arg;
    133    cinfo->created = PR_TRUE;
    134    cinfo->refCount = 1;
    135 
    136    rv = sec_pkcs7_init_content_info(cinfo, poolp, kind, detached);
    137    if (rv != SECSuccess) {
    138        PORT_FreeArena(poolp, PR_FALSE);
    139        return NULL;
    140    }
    141 
    142    return cinfo;
    143 }
    144 
    145 /*
    146 * Add a signer to a PKCS7 thing, verifying the signature cert first.
    147 * Any error returns SECFailure.
    148 *
    149 * XXX Right now this only adds the *first* signer.  It fails if you try
    150 * to add a second one -- this needs to be fixed.
    151 */
    152 static SECStatus
    153 sec_pkcs7_add_signer(SEC_PKCS7ContentInfo *cinfo,
    154                     CERTCertificate *cert,
    155                     SECCertUsage certusage,
    156                     CERTCertDBHandle *certdb,
    157                     SECOidTag digestalgtag,
    158                     SECItem *digestdata)
    159 {
    160    SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
    161    SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
    162    SECItem *digest, **digests, ***digestsp;
    163    SECItem *dummy;
    164    void *mark;
    165    SECStatus rv;
    166    SECOidTag kind;
    167 
    168    kind = SEC_PKCS7ContentType(cinfo);
    169    switch (kind) {
    170        case SEC_OID_PKCS7_SIGNED_DATA: {
    171            SEC_PKCS7SignedData *sdp;
    172 
    173            sdp = cinfo->content.signedData;
    174            digestalgsp = &(sdp->digestAlgorithms);
    175            digestsp = &(sdp->digests);
    176            signerinfosp = &(sdp->signerInfos);
    177        } break;
    178        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    179            SEC_PKCS7SignedAndEnvelopedData *saedp;
    180 
    181            saedp = cinfo->content.signedAndEnvelopedData;
    182            digestalgsp = &(saedp->digestAlgorithms);
    183            digestsp = &(saedp->digests);
    184            signerinfosp = &(saedp->signerInfos);
    185        } break;
    186        default:
    187            return SECFailure; /* XXX set an error? */
    188    }
    189 
    190    /*
    191     * XXX I think that CERT_VerifyCert should do this if *it* is passed
    192     * a NULL database.
    193     */
    194    if (certdb == NULL) {
    195        certdb = CERT_GetDefaultCertDB();
    196        if (certdb == NULL)
    197            return SECFailure; /* XXX set an error? */
    198    }
    199 
    200    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
    201                        cinfo->pwfn_arg, NULL) != SECSuccess) {
    202        /* XXX Did CERT_VerifyCert set an error? */
    203        return SECFailure;
    204    }
    205 
    206    /*
    207     * XXX This is the check that we do not already have a signer.
    208     * This is not what we really want -- we want to allow this
    209     * and *add* the new signer.
    210     */
    211    PORT_Assert(*signerinfosp == NULL && *digestalgsp == NULL && *digestsp == NULL);
    212    if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
    213        return SECFailure;
    214 
    215    mark = PORT_ArenaMark(cinfo->poolp);
    216 
    217    signerinfo = (SEC_PKCS7SignerInfo *)PORT_ArenaZAlloc(cinfo->poolp,
    218                                                         sizeof(SEC_PKCS7SignerInfo));
    219    if (signerinfo == NULL) {
    220        PORT_ArenaRelease(cinfo->poolp, mark);
    221        return SECFailure;
    222    }
    223 
    224    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &signerinfo->version,
    225                                  SEC_PKCS7_SIGNER_INFO_VERSION);
    226    if (dummy == NULL) {
    227        PORT_ArenaRelease(cinfo->poolp, mark);
    228        return SECFailure;
    229    }
    230    PORT_Assert(dummy == &signerinfo->version);
    231 
    232    signerinfo->cert = CERT_DupCertificate(cert);
    233    if (signerinfo->cert == NULL) {
    234        PORT_ArenaRelease(cinfo->poolp, mark);
    235        return SECFailure;
    236    }
    237 
    238    signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
    239    if (signerinfo->issuerAndSN == NULL) {
    240        PORT_ArenaRelease(cinfo->poolp, mark);
    241        return SECFailure;
    242    }
    243 
    244    rv = SECOID_SetAlgorithmID(cinfo->poolp, &signerinfo->digestAlg,
    245                               digestalgtag, NULL);
    246    if (rv != SECSuccess) {
    247        PORT_ArenaRelease(cinfo->poolp, mark);
    248        return SECFailure;
    249    }
    250 
    251    /*
    252     * Okay, now signerinfo is all set.  We just need to put it and its
    253     * companions (another copy of the digest algorithm, and the digest
    254     * itself if given) into the main structure.
    255     *
    256     * XXX If we are handling more than one signer, the following code
    257     * needs to look through the digest algorithms already specified
    258     * and see if the same one is there already.  If it is, it does not
    259     * need to be added again.  Also, if it is there *and* the digest
    260     * is not null, then the digest given should match the digest already
    261     * specified -- if not, that is an error.  Finally, the new signerinfo
    262     * should be *added* to the set already found.
    263     */
    264 
    265    signerinfos = (SEC_PKCS7SignerInfo **)PORT_ArenaAlloc(cinfo->poolp,
    266                                                          2 * sizeof(SEC_PKCS7SignerInfo *));
    267    if (signerinfos == NULL) {
    268        PORT_ArenaRelease(cinfo->poolp, mark);
    269        return SECFailure;
    270    }
    271    signerinfos[0] = signerinfo;
    272    signerinfos[1] = NULL;
    273 
    274    digestalg = PORT_ArenaZAlloc(cinfo->poolp, sizeof(SECAlgorithmID));
    275    digestalgs = PORT_ArenaAlloc(cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
    276    if (digestalg == NULL || digestalgs == NULL) {
    277        PORT_ArenaRelease(cinfo->poolp, mark);
    278        return SECFailure;
    279    }
    280    rv = SECOID_SetAlgorithmID(cinfo->poolp, digestalg, digestalgtag, NULL);
    281    if (rv != SECSuccess) {
    282        PORT_ArenaRelease(cinfo->poolp, mark);
    283        return SECFailure;
    284    }
    285    digestalgs[0] = digestalg;
    286    digestalgs[1] = NULL;
    287 
    288    if (digestdata != NULL) {
    289        digest = (SECItem *)PORT_ArenaAlloc(cinfo->poolp, sizeof(SECItem));
    290        digests = (SECItem **)PORT_ArenaAlloc(cinfo->poolp,
    291                                              2 * sizeof(SECItem *));
    292        if (digest == NULL || digests == NULL) {
    293            PORT_ArenaRelease(cinfo->poolp, mark);
    294            return SECFailure;
    295        }
    296        rv = SECITEM_CopyItem(cinfo->poolp, digest, digestdata);
    297        if (rv != SECSuccess) {
    298            PORT_ArenaRelease(cinfo->poolp, mark);
    299            return SECFailure;
    300        }
    301        digests[0] = digest;
    302        digests[1] = NULL;
    303    } else {
    304        digests = NULL;
    305    }
    306 
    307    *signerinfosp = signerinfos;
    308    *digestalgsp = digestalgs;
    309    *digestsp = digests;
    310 
    311    PORT_ArenaUnmark(cinfo->poolp, mark);
    312    return SECSuccess;
    313 }
    314 
    315 /*
    316 * Helper function for creating an empty signedData.
    317 */
    318 static SEC_PKCS7ContentInfo *
    319 sec_pkcs7_create_signed_data(SECKEYGetPasswordKey pwfn, void *pwfn_arg)
    320 {
    321    SEC_PKCS7ContentInfo *cinfo;
    322    SEC_PKCS7SignedData *sigd;
    323    SECStatus rv;
    324 
    325    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
    326                                          pwfn, pwfn_arg);
    327    if (cinfo == NULL)
    328        return NULL;
    329 
    330    sigd = cinfo->content.signedData;
    331    PORT_Assert(sigd != NULL);
    332 
    333    /*
    334     * XXX Might we want to allow content types other than data?
    335     * If so, via what interface?
    336     */
    337    rv = sec_pkcs7_init_content_info(&(sigd->contentInfo), cinfo->poolp,
    338                                     SEC_OID_PKCS7_DATA, PR_TRUE);
    339    if (rv != SECSuccess) {
    340        SEC_PKCS7DestroyContentInfo(cinfo);
    341        return NULL;
    342    }
    343 
    344    return cinfo;
    345 }
    346 
    347 /*
    348 * Start a PKCS7 signing context.
    349 *
    350 * "cert" is the cert that will be used to sign the data.  It will be
    351 * checked for validity.
    352 *
    353 * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
    354 * XXX Maybe SECCertUsage should be split so that our caller just says
    355 * "email" and *we* add the "signing" part -- otherwise our caller
    356 * could be lying about the usage; we do not want to allow encryption
    357 * certs for signing or vice versa.
    358 *
    359 * "certdb" is the cert database to use for verifying the cert.
    360 * It can be NULL if a default database is available (like in the client).
    361 *
    362 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
    363 *
    364 * "digest" is the actual digest of the data.  It must be provided in
    365 * the case of detached data or NULL if the content will be included.
    366 *
    367 * The return value can be passed to functions which add things to
    368 * it like attributes, then eventually to SEC_PKCS7Encode() or to
    369 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
    370 * SEC_PKCS7DestroyContentInfo().
    371 *
    372 * An error results in a return value of NULL and an error set.
    373 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
    374 */
    375 SEC_PKCS7ContentInfo *
    376 SEC_PKCS7CreateSignedData(CERTCertificate *cert,
    377                          SECCertUsage certusage,
    378                          CERTCertDBHandle *certdb,
    379                          SECOidTag digestalg,
    380                          SECItem *digest,
    381                          SECKEYGetPasswordKey pwfn, void *pwfn_arg)
    382 {
    383    SEC_PKCS7ContentInfo *cinfo;
    384    SECStatus rv;
    385 
    386    cinfo = sec_pkcs7_create_signed_data(pwfn, pwfn_arg);
    387    if (cinfo == NULL)
    388        return NULL;
    389 
    390    rv = sec_pkcs7_add_signer(cinfo, cert, certusage, certdb,
    391                              digestalg, digest);
    392    if (rv != SECSuccess) {
    393        SEC_PKCS7DestroyContentInfo(cinfo);
    394        return NULL;
    395    }
    396 
    397    return cinfo;
    398 }
    399 
    400 static SEC_PKCS7Attribute *
    401 sec_pkcs7_create_attribute(PLArenaPool *poolp, SECOidTag oidtag,
    402                           SECItem *value, PRBool encoded)
    403 {
    404    SEC_PKCS7Attribute *attr;
    405    SECItem **values;
    406    void *mark;
    407 
    408    PORT_Assert(poolp != NULL);
    409    mark = PORT_ArenaMark(poolp);
    410 
    411    attr = (SEC_PKCS7Attribute *)PORT_ArenaAlloc(poolp,
    412                                                 sizeof(SEC_PKCS7Attribute));
    413    if (attr == NULL)
    414        goto loser;
    415 
    416    attr->typeTag = SECOID_FindOIDByTag(oidtag);
    417    if (attr->typeTag == NULL)
    418        goto loser;
    419 
    420    if (SECITEM_CopyItem(poolp, &(attr->type),
    421                         &(attr->typeTag->oid)) != SECSuccess)
    422        goto loser;
    423 
    424    values = (SECItem **)PORT_ArenaAlloc(poolp, 2 * sizeof(SECItem *));
    425    if (values == NULL)
    426        goto loser;
    427 
    428    if (value != NULL) {
    429        SECItem *copy;
    430 
    431        copy = (SECItem *)PORT_ArenaAlloc(poolp, sizeof(SECItem));
    432        if (copy == NULL)
    433            goto loser;
    434 
    435        if (SECITEM_CopyItem(poolp, copy, value) != SECSuccess)
    436            goto loser;
    437 
    438        value = copy;
    439    }
    440 
    441    values[0] = value;
    442    values[1] = NULL;
    443    attr->values = values;
    444    attr->encoded = encoded;
    445 
    446    PORT_ArenaUnmark(poolp, mark);
    447    return attr;
    448 
    449 loser:
    450    PORT_Assert(mark != NULL);
    451    PORT_ArenaRelease(poolp, mark);
    452    return NULL;
    453 }
    454 
    455 static SECStatus
    456 sec_pkcs7_add_attribute(SEC_PKCS7ContentInfo *cinfo,
    457                        SEC_PKCS7Attribute ***attrsp,
    458                        SEC_PKCS7Attribute *attr)
    459 {
    460    SEC_PKCS7Attribute **attrs;
    461    SECItem *ct_value;
    462    void *mark;
    463 
    464    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    465    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
    466        return SECFailure;
    467 
    468    attrs = *attrsp;
    469    if (attrs != NULL) {
    470        int count;
    471 
    472        /*
    473         * We already have some attributes, and just need to add this
    474         * new one.
    475         */
    476 
    477        /*
    478         * We should already have the *required* attributes, which were
    479         * created/added at the same time the first attribute was added.
    480         */
    481        PORT_Assert(sec_PKCS7FindAttribute(attrs,
    482                                           SEC_OID_PKCS9_CONTENT_TYPE,
    483                                           PR_FALSE) != NULL);
    484        PORT_Assert(sec_PKCS7FindAttribute(attrs,
    485                                           SEC_OID_PKCS9_MESSAGE_DIGEST,
    486                                           PR_FALSE) != NULL);
    487 
    488        for (count = 0; attrs[count] != NULL; count++)
    489            ;
    490        attrs = (SEC_PKCS7Attribute **)PORT_ArenaGrow(cinfo->poolp, attrs,
    491                                                      (count + 1) * sizeof(SEC_PKCS7Attribute *),
    492                                                      (count + 2) * sizeof(SEC_PKCS7Attribute *));
    493        if (attrs == NULL)
    494            return SECFailure;
    495 
    496        attrs[count] = attr;
    497        attrs[count + 1] = NULL;
    498        *attrsp = attrs;
    499 
    500        return SECSuccess;
    501    }
    502 
    503    /*
    504     * This is the first time an attribute is going in.
    505     * We need to create and add the required attributes, and then
    506     * we will also add in the one our caller gave us.
    507     */
    508 
    509    /*
    510     * There are 2 required attributes, plus the one our caller wants
    511     * to add, plus we always end with a NULL one.  Thus, four slots.
    512     */
    513    attrs = (SEC_PKCS7Attribute **)PORT_ArenaAlloc(cinfo->poolp,
    514                                                   4 * sizeof(SEC_PKCS7Attribute *));
    515    if (attrs == NULL)
    516        return SECFailure;
    517 
    518    mark = PORT_ArenaMark(cinfo->poolp);
    519 
    520    /*
    521     * First required attribute is the content type of the data
    522     * being signed.
    523     */
    524    ct_value = &(cinfo->content.signedData->contentInfo.contentType);
    525    attrs[0] = sec_pkcs7_create_attribute(cinfo->poolp,
    526                                          SEC_OID_PKCS9_CONTENT_TYPE,
    527                                          ct_value, PR_FALSE);
    528    /*
    529     * Second required attribute is the message digest of the data
    530     * being signed; we leave the value NULL for now (just create
    531     * the place for it to go), and the encoder will fill it in later.
    532     */
    533    attrs[1] = sec_pkcs7_create_attribute(cinfo->poolp,
    534                                          SEC_OID_PKCS9_MESSAGE_DIGEST,
    535                                          NULL, PR_FALSE);
    536    if (attrs[0] == NULL || attrs[1] == NULL) {
    537        PORT_ArenaRelease(cinfo->poolp, mark);
    538        return SECFailure;
    539    }
    540 
    541    attrs[2] = attr;
    542    attrs[3] = NULL;
    543    *attrsp = attrs;
    544 
    545    PORT_ArenaUnmark(cinfo->poolp, mark);
    546    return SECSuccess;
    547 }
    548 
    549 /*
    550 * Add the signing time to the authenticated (i.e. signed) attributes
    551 * of "cinfo".  This is expected to be included in outgoing signed
    552 * messages for email (S/MIME) but is likely useful in other situations.
    553 *
    554 * This should only be added once; a second call will either do
    555 * nothing or replace an old signing time with a newer one.
    556 *
    557 * XXX This will probably just shove the current time into "cinfo"
    558 * but it will not actually get signed until the entire item is
    559 * processed for encoding.  Is this (expected to be small) delay okay?
    560 *
    561 * "cinfo" should be of type signedData (the only kind of pkcs7 data
    562 * that is allowed authenticated attributes); SECFailure will be returned
    563 * if it is not.
    564 */
    565 SECStatus
    566 SEC_PKCS7AddSigningTime(SEC_PKCS7ContentInfo *cinfo)
    567 {
    568    SEC_PKCS7SignerInfo **signerinfos;
    569    SEC_PKCS7Attribute *attr;
    570    SECItem stime;
    571    SECStatus rv;
    572    int si;
    573 
    574    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    575    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
    576        return SECFailure;
    577 
    578    signerinfos = cinfo->content.signedData->signerInfos;
    579 
    580    /* There has to be a signer, or it makes no sense. */
    581    if (signerinfos == NULL || signerinfos[0] == NULL)
    582        return SECFailure;
    583 
    584    rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
    585    if (rv != SECSuccess)
    586        return rv;
    587 
    588    attr = sec_pkcs7_create_attribute(cinfo->poolp,
    589                                      SEC_OID_PKCS9_SIGNING_TIME,
    590                                      &stime, PR_FALSE);
    591    SECITEM_FreeItem(&stime, PR_FALSE);
    592 
    593    if (attr == NULL)
    594        return SECFailure;
    595 
    596    rv = SECSuccess;
    597    for (si = 0; signerinfos[si] != NULL; si++) {
    598        SEC_PKCS7Attribute *oattr;
    599 
    600        oattr = sec_PKCS7FindAttribute(signerinfos[si]->authAttr,
    601                                       SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
    602        PORT_Assert(oattr == NULL);
    603        if (oattr != NULL)
    604            continue; /* XXX or would it be better to replace it? */
    605 
    606        rv = sec_pkcs7_add_attribute(cinfo, &(signerinfos[si]->authAttr),
    607                                     attr);
    608        if (rv != SECSuccess)
    609            break; /* could try to continue, but may as well give up now */
    610    }
    611 
    612    return rv;
    613 }
    614 
    615 /*
    616 * Add the specified attribute to the authenticated (i.e. signed) attributes
    617 * of "cinfo" -- "oidtag" describes the attribute and "value" is the
    618 * value to be associated with it.  NOTE! "value" must already be encoded;
    619 * no interpretation of "oidtag" is done.  Also, it is assumed that this
    620 * signedData has only one signer -- if we ever need to add attributes
    621 * when there is more than one signature, we need a way to specify *which*
    622 * signature should get the attribute.
    623 *
    624 * XXX Technically, a signed attribute can have multiple values; if/when
    625 * we ever need to support an attribute which takes multiple values, we
    626 * either need to change this interface or create an AddSignedAttributeValue
    627 * which can be called subsequently, and would then append a value.
    628 *
    629 * "cinfo" should be of type signedData (the only kind of pkcs7 data
    630 * that is allowed authenticated attributes); SECFailure will be returned
    631 * if it is not.
    632 */
    633 SECStatus
    634 SEC_PKCS7AddSignedAttribute(SEC_PKCS7ContentInfo *cinfo,
    635                            SECOidTag oidtag,
    636                            SECItem *value)
    637 {
    638    SEC_PKCS7SignerInfo **signerinfos;
    639    SEC_PKCS7Attribute *attr;
    640 
    641    PORT_Assert(SEC_PKCS7ContentType(cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
    642    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
    643        return SECFailure;
    644 
    645    signerinfos = cinfo->content.signedData->signerInfos;
    646 
    647    /*
    648     * No signature or more than one means no deal.
    649     */
    650    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
    651        return SECFailure;
    652 
    653    attr = sec_pkcs7_create_attribute(cinfo->poolp, oidtag, value, PR_TRUE);
    654    if (attr == NULL)
    655        return SECFailure;
    656 
    657    return sec_pkcs7_add_attribute(cinfo, &(signerinfos[0]->authAttr), attr);
    658 }
    659 
    660 /*
    661 * Mark that the signer certificates and their issuing chain should
    662 * be included in the encoded data.  This is expected to be used
    663 * in outgoing signed messages for email (S/MIME).
    664 *
    665 * "certdb" is the cert database to use for finding the chain.
    666 * It can be NULL, meaning use the default database.
    667 *
    668 * "cinfo" should be of type signedData or signedAndEnvelopedData;
    669 * SECFailure will be returned if it is not.
    670 */
    671 SECStatus
    672 SEC_PKCS7IncludeCertChain(SEC_PKCS7ContentInfo *cinfo,
    673                          CERTCertDBHandle *certdb)
    674 {
    675    SECOidTag kind;
    676    SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
    677 
    678    kind = SEC_PKCS7ContentType(cinfo);
    679    switch (kind) {
    680        case SEC_OID_PKCS7_SIGNED_DATA:
    681            signerinfos = cinfo->content.signedData->signerInfos;
    682            break;
    683        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    684            signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
    685            break;
    686        default:
    687            return SECFailure; /* XXX set an error? */
    688    }
    689 
    690    if (signerinfos == NULL) /* no signer, no certs? */
    691        return SECFailure;   /* XXX set an error? */
    692 
    693    if (certdb == NULL) {
    694        certdb = CERT_GetDefaultCertDB();
    695        if (certdb == NULL) {
    696            PORT_SetError(SEC_ERROR_BAD_DATABASE);
    697            return SECFailure;
    698        }
    699    }
    700 
    701    /* XXX Should it be an error if we find no signerinfo or no certs? */
    702    while ((signerinfo = *signerinfos++) != NULL) {
    703        if (signerinfo->cert != NULL)
    704            /* get the cert chain.  don't send the root to avoid contamination
    705             * of old clients with a new root that they don't trust
    706             */
    707            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
    708                                                          certUsageEmailSigner,
    709                                                          PR_FALSE);
    710    }
    711 
    712    return SECSuccess;
    713 }
    714 
    715 /*
    716 * Helper function to add a certificate chain for inclusion in the
    717 * bag of certificates in a signedData.
    718 */
    719 static SECStatus
    720 sec_pkcs7_add_cert_chain(SEC_PKCS7ContentInfo *cinfo,
    721                         CERTCertificate *cert,
    722                         CERTCertDBHandle *certdb)
    723 {
    724    SECOidTag kind;
    725    CERTCertificateList *certlist, **certlists, ***certlistsp;
    726    int count;
    727 
    728    kind = SEC_PKCS7ContentType(cinfo);
    729    switch (kind) {
    730        case SEC_OID_PKCS7_SIGNED_DATA: {
    731            SEC_PKCS7SignedData *sdp;
    732 
    733            sdp = cinfo->content.signedData;
    734            certlistsp = &(sdp->certLists);
    735        } break;
    736        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    737            SEC_PKCS7SignedAndEnvelopedData *saedp;
    738 
    739            saedp = cinfo->content.signedAndEnvelopedData;
    740            certlistsp = &(saedp->certLists);
    741        } break;
    742        default:
    743            return SECFailure; /* XXX set an error? */
    744    }
    745 
    746    if (certdb == NULL) {
    747        certdb = CERT_GetDefaultCertDB();
    748        if (certdb == NULL) {
    749            PORT_SetError(SEC_ERROR_BAD_DATABASE);
    750            return SECFailure;
    751        }
    752    }
    753 
    754    certlist = CERT_CertChainFromCert(cert, certUsageEmailSigner, PR_FALSE);
    755    if (certlist == NULL)
    756        return SECFailure;
    757 
    758    certlists = *certlistsp;
    759    if (certlists == NULL) {
    760        count = 0;
    761        certlists = (CERTCertificateList **)PORT_ArenaAlloc(cinfo->poolp,
    762                                                            2 * sizeof(CERTCertificateList *));
    763    } else {
    764        for (count = 0; certlists[count] != NULL; count++)
    765            ;
    766        PORT_Assert(count); /* should be at least one already */
    767        certlists = (CERTCertificateList **)PORT_ArenaGrow(cinfo->poolp,
    768                                                           certlists,
    769                                                           (count + 1) * sizeof(CERTCertificateList *),
    770                                                           (count + 2) * sizeof(CERTCertificateList *));
    771    }
    772 
    773    if (certlists == NULL) {
    774        CERT_DestroyCertificateList(certlist);
    775        return SECFailure;
    776    }
    777 
    778    certlists[count] = certlist;
    779    certlists[count + 1] = NULL;
    780 
    781    *certlistsp = certlists;
    782 
    783    return SECSuccess;
    784 }
    785 
    786 /*
    787 * Helper function to add a certificate for inclusion in the bag of
    788 * certificates in a signedData.
    789 */
    790 static SECStatus
    791 sec_pkcs7_add_certificate(SEC_PKCS7ContentInfo *cinfo,
    792                          CERTCertificate *cert)
    793 {
    794    SECOidTag kind;
    795    CERTCertificate **certs, ***certsp;
    796    int count;
    797 
    798    kind = SEC_PKCS7ContentType(cinfo);
    799    switch (kind) {
    800        case SEC_OID_PKCS7_SIGNED_DATA: {
    801            SEC_PKCS7SignedData *sdp;
    802 
    803            sdp = cinfo->content.signedData;
    804            certsp = &(sdp->certs);
    805        } break;
    806        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    807            SEC_PKCS7SignedAndEnvelopedData *saedp;
    808 
    809            saedp = cinfo->content.signedAndEnvelopedData;
    810            certsp = &(saedp->certs);
    811        } break;
    812        default:
    813            return SECFailure; /* XXX set an error? */
    814    }
    815 
    816    cert = CERT_DupCertificate(cert);
    817    if (cert == NULL)
    818        return SECFailure;
    819 
    820    certs = *certsp;
    821    if (certs == NULL) {
    822        count = 0;
    823        certs = (CERTCertificate **)PORT_ArenaAlloc(cinfo->poolp,
    824                                                    2 * sizeof(CERTCertificate *));
    825    } else {
    826        for (count = 0; certs[count] != NULL; count++)
    827            ;
    828        PORT_Assert(count); /* should be at least one already */
    829        certs = (CERTCertificate **)PORT_ArenaGrow(cinfo->poolp, certs,
    830                                                   (count + 1) * sizeof(CERTCertificate *),
    831                                                   (count + 2) * sizeof(CERTCertificate *));
    832    }
    833 
    834    if (certs == NULL) {
    835        CERT_DestroyCertificate(cert);
    836        return SECFailure;
    837    }
    838 
    839    certs[count] = cert;
    840    certs[count + 1] = NULL;
    841 
    842    *certsp = certs;
    843 
    844    return SECSuccess;
    845 }
    846 
    847 /*
    848 * Create a PKCS7 certs-only container.
    849 *
    850 * "cert" is the (first) cert that will be included.
    851 *
    852 * "include_chain" specifies whether the entire chain for "cert" should
    853 * be included.
    854 *
    855 * "certdb" is the cert database to use for finding the chain.
    856 * It can be NULL in when "include_chain" is false, or when meaning
    857 * use the default database.
    858 *
    859 * More certs and chains can be added via AddCertificate and AddCertChain.
    860 *
    861 * An error results in a return value of NULL and an error set.
    862 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
    863 */
    864 SEC_PKCS7ContentInfo *
    865 SEC_PKCS7CreateCertsOnly(CERTCertificate *cert,
    866                         PRBool include_chain,
    867                         CERTCertDBHandle *certdb)
    868 {
    869    SEC_PKCS7ContentInfo *cinfo;
    870    SECStatus rv;
    871 
    872    cinfo = sec_pkcs7_create_signed_data(NULL, NULL);
    873    if (cinfo == NULL)
    874        return NULL;
    875 
    876    if (include_chain)
    877        rv = sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
    878    else
    879        rv = sec_pkcs7_add_certificate(cinfo, cert);
    880 
    881    if (rv != SECSuccess) {
    882        SEC_PKCS7DestroyContentInfo(cinfo);
    883        return NULL;
    884    }
    885 
    886    return cinfo;
    887 }
    888 
    889 /*
    890 * Add "cert" and its entire chain to the set of certs included in "cinfo".
    891 *
    892 * "certdb" is the cert database to use for finding the chain.
    893 * It can be NULL, meaning use the default database.
    894 *
    895 * "cinfo" should be of type signedData or signedAndEnvelopedData;
    896 * SECFailure will be returned if it is not.
    897 */
    898 SECStatus
    899 SEC_PKCS7AddCertChain(SEC_PKCS7ContentInfo *cinfo,
    900                      CERTCertificate *cert,
    901                      CERTCertDBHandle *certdb)
    902 {
    903    SECOidTag kind;
    904 
    905    kind = SEC_PKCS7ContentType(cinfo);
    906    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
    907        return SECFailure; /* XXX set an error? */
    908 
    909    return sec_pkcs7_add_cert_chain(cinfo, cert, certdb);
    910 }
    911 
    912 /*
    913 * Add "cert" to the set of certs included in "cinfo".
    914 *
    915 * "cinfo" should be of type signedData or signedAndEnvelopedData;
    916 * SECFailure will be returned if it is not.
    917 */
    918 SECStatus
    919 SEC_PKCS7AddCertificate(SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
    920 {
    921    SECOidTag kind;
    922 
    923    kind = SEC_PKCS7ContentType(cinfo);
    924    if (kind != SEC_OID_PKCS7_SIGNED_DATA && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
    925        return SECFailure; /* XXX set an error? */
    926 
    927    return sec_pkcs7_add_certificate(cinfo, cert);
    928 }
    929 
    930 static SECStatus
    931 sec_pkcs7_init_encrypted_content_info(SEC_PKCS7EncryptedContentInfo *enccinfo,
    932                                      PLArenaPool *poolp,
    933                                      SECOidTag kind, PRBool detached,
    934                                      SECOidTag encalg, int keysize)
    935 {
    936    SECStatus rv;
    937 
    938    PORT_Assert(enccinfo != NULL && poolp != NULL);
    939    if (enccinfo == NULL || poolp == NULL)
    940        return SECFailure;
    941 
    942    /*
    943     * XXX Some day we may want to allow for other kinds.  That needs
    944     * more work and modifications to the creation interface, etc.
    945     * For now, allow but notice callers who pass in other kinds.
    946     * They are responsible for creating the inner type and encoding,
    947     * if it is other than DATA.
    948     */
    949    PORT_Assert(kind == SEC_OID_PKCS7_DATA);
    950 
    951    enccinfo->contentTypeTag = SECOID_FindOIDByTag(kind);
    952    PORT_Assert(enccinfo->contentTypeTag && enccinfo->contentTypeTag->offset == kind);
    953 
    954    rv = SECITEM_CopyItem(poolp, &(enccinfo->contentType),
    955                          &(enccinfo->contentTypeTag->oid));
    956    if (rv != SECSuccess)
    957        return rv;
    958 
    959    /* Save keysize and algorithm for later. */
    960    enccinfo->keysize = keysize;
    961    enccinfo->encalg = encalg;
    962 
    963    return SECSuccess;
    964 }
    965 
    966 /*
    967 * Add a recipient to a PKCS7 thing, verifying their cert first.
    968 * Any error returns SECFailure.
    969 */
    970 static SECStatus
    971 sec_pkcs7_add_recipient(SEC_PKCS7ContentInfo *cinfo,
    972                        CERTCertificate *cert,
    973                        SECCertUsage certusage,
    974                        CERTCertDBHandle *certdb)
    975 {
    976    SECOidTag kind;
    977    SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
    978    SECItem *dummy;
    979    void *mark;
    980    int count;
    981 
    982    kind = SEC_PKCS7ContentType(cinfo);
    983    switch (kind) {
    984        case SEC_OID_PKCS7_ENVELOPED_DATA: {
    985            SEC_PKCS7EnvelopedData *edp;
    986 
    987            edp = cinfo->content.envelopedData;
    988            recipientinfosp = &(edp->recipientInfos);
    989        } break;
    990        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    991            SEC_PKCS7SignedAndEnvelopedData *saedp;
    992 
    993            saedp = cinfo->content.signedAndEnvelopedData;
    994            recipientinfosp = &(saedp->recipientInfos);
    995        } break;
    996        default:
    997            return SECFailure; /* XXX set an error? */
    998    }
    999 
   1000    /*
   1001     * XXX I think that CERT_VerifyCert should do this if *it* is passed
   1002     * a NULL database.
   1003     */
   1004    if (certdb == NULL) {
   1005        certdb = CERT_GetDefaultCertDB();
   1006        if (certdb == NULL)
   1007            return SECFailure; /* XXX set an error? */
   1008    }
   1009 
   1010    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, PR_Now(),
   1011                        cinfo->pwfn_arg, NULL) != SECSuccess) {
   1012        /* XXX Did CERT_VerifyCert set an error? */
   1013        return SECFailure;
   1014    }
   1015 
   1016    mark = PORT_ArenaMark(cinfo->poolp);
   1017 
   1018    recipientinfo = (SEC_PKCS7RecipientInfo *)PORT_ArenaZAlloc(cinfo->poolp,
   1019                                                               sizeof(SEC_PKCS7RecipientInfo));
   1020    if (recipientinfo == NULL) {
   1021        PORT_ArenaRelease(cinfo->poolp, mark);
   1022        return SECFailure;
   1023    }
   1024 
   1025    dummy = SEC_ASN1EncodeInteger(cinfo->poolp, &recipientinfo->version,
   1026                                  SEC_PKCS7_RECIPIENT_INFO_VERSION);
   1027    if (dummy == NULL) {
   1028        PORT_ArenaRelease(cinfo->poolp, mark);
   1029        return SECFailure;
   1030    }
   1031    PORT_Assert(dummy == &recipientinfo->version);
   1032 
   1033    recipientinfo->cert = CERT_DupCertificate(cert);
   1034    if (recipientinfo->cert == NULL) {
   1035        PORT_ArenaRelease(cinfo->poolp, mark);
   1036        return SECFailure;
   1037    }
   1038 
   1039    recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN(cinfo->poolp, cert);
   1040    if (recipientinfo->issuerAndSN == NULL) {
   1041        PORT_ArenaRelease(cinfo->poolp, mark);
   1042        return SECFailure;
   1043    }
   1044 
   1045    /*
   1046     * Okay, now recipientinfo is all set.  We just need to put it into
   1047     * the main structure.
   1048     *
   1049     * If this is the first recipient, allocate a new recipientinfos array;
   1050     * otherwise, reallocate the array, making room for the new entry.
   1051     */
   1052    recipientinfos = *recipientinfosp;
   1053    if (recipientinfos == NULL) {
   1054        count = 0;
   1055        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc(
   1056            cinfo->poolp,
   1057            2 * sizeof(SEC_PKCS7RecipientInfo *));
   1058    } else {
   1059        for (count = 0; recipientinfos[count] != NULL; count++)
   1060            ;
   1061        PORT_Assert(count); /* should be at least one already */
   1062        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow(
   1063            cinfo->poolp, recipientinfos,
   1064            (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
   1065            (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
   1066    }
   1067 
   1068    if (recipientinfos == NULL) {
   1069        PORT_ArenaRelease(cinfo->poolp, mark);
   1070        return SECFailure;
   1071    }
   1072 
   1073    recipientinfos[count] = recipientinfo;
   1074    recipientinfos[count + 1] = NULL;
   1075 
   1076    *recipientinfosp = recipientinfos;
   1077 
   1078    PORT_ArenaUnmark(cinfo->poolp, mark);
   1079    return SECSuccess;
   1080 }
   1081 
   1082 /*
   1083 * Start a PKCS7 enveloping context.
   1084 *
   1085 * "cert" is the cert for the recipient.  It will be checked for validity.
   1086 *
   1087 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
   1088 * XXX Maybe SECCertUsage should be split so that our caller just says
   1089 * "email" and *we* add the "recipient" part -- otherwise our caller
   1090 * could be lying about the usage; we do not want to allow encryption
   1091 * certs for signing or vice versa.
   1092 *
   1093 * "certdb" is the cert database to use for verifying the cert.
   1094 * It can be NULL if a default database is available (like in the client).
   1095 *
   1096 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
   1097 *
   1098 * "keysize" specifies the bulk encryption key size, in bits.
   1099 *
   1100 * The return value can be passed to functions which add things to
   1101 * it like more recipients, then eventually to SEC_PKCS7Encode() or to
   1102 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
   1103 * SEC_PKCS7DestroyContentInfo().
   1104 *
   1105 * An error results in a return value of NULL and an error set.
   1106 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
   1107 */
   1108 extern SEC_PKCS7ContentInfo *
   1109 SEC_PKCS7CreateEnvelopedData(CERTCertificate *cert,
   1110                             SECCertUsage certusage,
   1111                             CERTCertDBHandle *certdb,
   1112                             SECOidTag encalg,
   1113                             int keysize,
   1114                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
   1115 {
   1116    SEC_PKCS7ContentInfo *cinfo;
   1117    SEC_PKCS7EnvelopedData *envd;
   1118    SECStatus rv;
   1119 
   1120    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENVELOPED_DATA,
   1121                                          PR_FALSE, pwfn, pwfn_arg);
   1122    if (cinfo == NULL)
   1123        return NULL;
   1124 
   1125    rv = sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
   1126    if (rv != SECSuccess) {
   1127        SEC_PKCS7DestroyContentInfo(cinfo);
   1128        return NULL;
   1129    }
   1130 
   1131    envd = cinfo->content.envelopedData;
   1132    PORT_Assert(envd != NULL);
   1133 
   1134    /*
   1135     * XXX Might we want to allow content types other than data?
   1136     * If so, via what interface?
   1137     */
   1138    rv = sec_pkcs7_init_encrypted_content_info(&(envd->encContentInfo),
   1139                                               cinfo->poolp,
   1140                                               SEC_OID_PKCS7_DATA, PR_FALSE,
   1141                                               encalg, keysize);
   1142    if (rv != SECSuccess) {
   1143        SEC_PKCS7DestroyContentInfo(cinfo);
   1144        return NULL;
   1145    }
   1146 
   1147    /* XXX Anything more to do here? */
   1148 
   1149    return cinfo;
   1150 }
   1151 
   1152 /*
   1153 * Add another recipient to an encrypted message.
   1154 *
   1155 * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
   1156 * SECFailure will be returned if it is not.
   1157 *
   1158 * "cert" is the cert for the recipient.  It will be checked for validity.
   1159 *
   1160 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
   1161 * XXX Maybe SECCertUsage should be split so that our caller just says
   1162 * "email" and *we* add the "recipient" part -- otherwise our caller
   1163 * could be lying about the usage; we do not want to allow encryption
   1164 * certs for signing or vice versa.
   1165 *
   1166 * "certdb" is the cert database to use for verifying the cert.
   1167 * It can be NULL if a default database is available (like in the client).
   1168 */
   1169 SECStatus
   1170 SEC_PKCS7AddRecipient(SEC_PKCS7ContentInfo *cinfo,
   1171                      CERTCertificate *cert,
   1172                      SECCertUsage certusage,
   1173                      CERTCertDBHandle *certdb)
   1174 {
   1175    return sec_pkcs7_add_recipient(cinfo, cert, certusage, certdb);
   1176 }
   1177 
   1178 /*
   1179 * Create an empty PKCS7 data content info.
   1180 *
   1181 * An error results in a return value of NULL and an error set.
   1182 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
   1183 */
   1184 SEC_PKCS7ContentInfo *
   1185 SEC_PKCS7CreateData(void)
   1186 {
   1187    return sec_pkcs7_create_content_info(SEC_OID_PKCS7_DATA, PR_FALSE,
   1188                                         NULL, NULL);
   1189 }
   1190 
   1191 /*
   1192 * Create an empty PKCS7 encrypted content info.
   1193 *
   1194 * "algorithm" specifies the bulk encryption algorithm to use.
   1195 *
   1196 * An error results in a return value of NULL and an error set.
   1197 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
   1198 */
   1199 SEC_PKCS7ContentInfo *
   1200 SEC_PKCS7CreateEncryptedData(SECOidTag algorithm, int keysize,
   1201                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
   1202 {
   1203    SEC_PKCS7ContentInfo *cinfo;
   1204    SECAlgorithmID *algid;
   1205    SEC_PKCS7EncryptedData *enc_data;
   1206    SECStatus rv;
   1207 
   1208    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
   1209                                          PR_FALSE, pwfn, pwfn_arg);
   1210    if (cinfo == NULL)
   1211        return NULL;
   1212 
   1213    enc_data = cinfo->content.encryptedData;
   1214    algid = &(enc_data->encContentInfo.contentEncAlg);
   1215 
   1216    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
   1217        rv = SECOID_SetAlgorithmID(cinfo->poolp, algid, algorithm, NULL);
   1218    } else {
   1219        /* Assume password-based-encryption.
   1220         * Note: we can't generate pkcs5v2 from this interface.
   1221         * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
   1222         * non-PBE oids and assuming that they are pkcs5v2 oids, but
   1223         * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
   1224         * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
   1225         * to create pkcs5v2 PBEs */
   1226        SECAlgorithmID *pbe_algid;
   1227        pbe_algid = PK11_CreatePBEAlgorithmID(algorithm,
   1228                                              NSS_PBE_DEFAULT_ITERATION_COUNT,
   1229                                              NULL);
   1230        if (pbe_algid == NULL) {
   1231            rv = SECFailure;
   1232        } else {
   1233            rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
   1234            SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
   1235        }
   1236    }
   1237 
   1238    if (rv != SECSuccess) {
   1239        SEC_PKCS7DestroyContentInfo(cinfo);
   1240        return NULL;
   1241    }
   1242 
   1243    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
   1244                                               cinfo->poolp,
   1245                                               SEC_OID_PKCS7_DATA, PR_FALSE,
   1246                                               algorithm, keysize);
   1247    if (rv != SECSuccess) {
   1248        SEC_PKCS7DestroyContentInfo(cinfo);
   1249        return NULL;
   1250    }
   1251 
   1252    return cinfo;
   1253 }
   1254 
   1255 SEC_PKCS7ContentInfo *
   1256 SEC_PKCS7CreateEncryptedDataWithPBEV2(SECOidTag pbe_algorithm,
   1257                                      SECOidTag cipher_algorithm,
   1258                                      SECOidTag prf_algorithm,
   1259                                      int keysize,
   1260                                      SECKEYGetPasswordKey pwfn, void *pwfn_arg)
   1261 {
   1262    SEC_PKCS7ContentInfo *cinfo;
   1263    SECAlgorithmID *algid;
   1264    SEC_PKCS7EncryptedData *enc_data;
   1265    SECStatus rv;
   1266 
   1267    PORT_Assert(SEC_PKCS5IsAlgorithmPBEAlgTag(pbe_algorithm));
   1268 
   1269    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
   1270                                          PR_FALSE, pwfn, pwfn_arg);
   1271    if (cinfo == NULL)
   1272        return NULL;
   1273 
   1274    enc_data = cinfo->content.encryptedData;
   1275    algid = &(enc_data->encContentInfo.contentEncAlg);
   1276 
   1277    SECAlgorithmID *pbe_algid;
   1278    pbe_algid = PK11_CreatePBEV2AlgorithmID(pbe_algorithm,
   1279                                            cipher_algorithm,
   1280                                            prf_algorithm,
   1281                                            keysize,
   1282                                            NSS_PBE_DEFAULT_ITERATION_COUNT,
   1283                                            NULL);
   1284    if (pbe_algid == NULL) {
   1285        rv = SECFailure;
   1286    } else {
   1287        rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
   1288        SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
   1289    }
   1290 
   1291    if (rv != SECSuccess) {
   1292        SEC_PKCS7DestroyContentInfo(cinfo);
   1293        return NULL;
   1294    }
   1295 
   1296    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
   1297                                               cinfo->poolp,
   1298                                               SEC_OID_PKCS7_DATA, PR_FALSE,
   1299                                               cipher_algorithm, keysize);
   1300    if (rv != SECSuccess) {
   1301        SEC_PKCS7DestroyContentInfo(cinfo);
   1302        return NULL;
   1303    }
   1304 
   1305    return cinfo;
   1306 }