tor-browser

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

p7decode.c (66916B)


      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 decoding, verification.
      7 */
      8 
      9 #include "p7local.h"
     10 
     11 #include "cert.h"
     12 /* XXX do not want to have to include */
     13 #include "certdb.h" /* certdb.h -- the trust stuff needed by */
     14                    /* the add certificate code needs to get */
     15                    /* rewritten/abstracted and then this */
     16                    /* include should be removed! */
     17 /*#include "cdbhdl.h" */
     18 #include "cryptohi.h"
     19 #include "keyhi.h"
     20 #include "secasn1.h"
     21 #include "secitem.h"
     22 #include "secoid.h"
     23 #include "pk11func.h"
     24 #include "prtime.h"
     25 #include "secerr.h"
     26 #include "sechash.h" /* for HASH_GetHashObject() */
     27 #include "secder.h"
     28 #include "secpkcs5.h"
     29 
     30 struct sec_pkcs7_decoder_worker {
     31    int depth;
     32    int digcnt;
     33    void **digcxs;
     34    const SECHashObject **digobjs;
     35    sec_PKCS7CipherObject *decryptobj;
     36    PRBool saw_contents;
     37 };
     38 
     39 struct SEC_PKCS7DecoderContextStr {
     40    SEC_ASN1DecoderContext *dcx;
     41    SEC_PKCS7ContentInfo *cinfo;
     42    SEC_PKCS7DecoderContentCallback cb;
     43    void *cb_arg;
     44    SECKEYGetPasswordKey pwfn;
     45    void *pwfn_arg;
     46    struct sec_pkcs7_decoder_worker worker;
     47    PLArenaPool *tmp_poolp;
     48    int error;
     49    SEC_PKCS7GetDecryptKeyCallback dkcb;
     50    void *dkcb_arg;
     51    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;
     52 };
     53 
     54 /*
     55 * Handle one worker, decrypting and digesting the data as necessary.
     56 *
     57 * XXX If/when we support nested contents, this probably needs to be
     58 * revised somewhat to get passed the content-info (which unfortunately
     59 * can be two different types depending on whether it is encrypted or not)
     60 * corresponding to the given worker.
     61 */
     62 static void
     63 sec_pkcs7_decoder_work_data(SEC_PKCS7DecoderContext *p7dcx,
     64                            struct sec_pkcs7_decoder_worker *worker,
     65                            const unsigned char *data, unsigned long len,
     66                            PRBool final)
     67 {
     68    unsigned char *buf = NULL;
     69    PRBool freeBuf = PR_FALSE;
     70    SECStatus rv;
     71    int i;
     72 
     73    /*
     74     * We should really have data to process, or we should be trying
     75     * to finish/flush the last block.  (This is an overly paranoid
     76     * check since all callers are in this file and simple inspection
     77     * proves they do it right.  But it could find a bug in future
     78     * modifications/development, that is why it is here.)
     79     */
     80    PORT_Assert((data != NULL && len) || final);
     81 
     82    /*
     83     * Decrypt this chunk.
     84     *
     85     * XXX If we get an error, we do not want to do the digest or callback,
     86     * but we want to keep decoding.  Or maybe we want to stop decoding
     87     * altogether if there is a callback, because obviously we are not
     88     * sending the data back and they want to know that.
     89     */
     90    if (worker->decryptobj != NULL) {
     91        /* XXX the following lengths should all be longs? */
     92        unsigned int inlen;  /* length of data being decrypted */
     93        unsigned int outlen; /* length of decrypted data */
     94        unsigned int buflen; /* length available for decrypted data */
     95        SECItem *plain;
     96 
     97        inlen = len;
     98        buflen = sec_PKCS7DecryptLength(worker->decryptobj, inlen, final);
     99        if (buflen == 0) {
    100            if (inlen == 0) /* no input and no output */
    101                return;
    102            /*
    103             * No output is expected, but the input data may be buffered
    104             * so we still have to call Decrypt.
    105             */
    106            rv = sec_PKCS7Decrypt(worker->decryptobj, NULL, NULL, 0,
    107                                  data, inlen, final);
    108            if (rv != SECSuccess) {
    109                p7dcx->error = PORT_GetError();
    110                return; /* XXX indicate error? */
    111            }
    112            return;
    113        }
    114 
    115        if (p7dcx->cb != NULL) {
    116            buf = (unsigned char *)PORT_Alloc(buflen);
    117            freeBuf = PR_TRUE;
    118            plain = NULL;
    119        } else {
    120            unsigned long oldlen;
    121 
    122            /*
    123             * XXX This assumes one level of content only.
    124             * See comment above about nested content types.
    125             * XXX Also, it should work for signedAndEnvelopedData, too!
    126             */
    127            plain = &(p7dcx->cinfo->content.envelopedData->encContentInfo.plainContent);
    128 
    129            oldlen = plain->len;
    130            if (oldlen == 0) {
    131                buf = (unsigned char *)PORT_ArenaAlloc(p7dcx->cinfo->poolp,
    132                                                       buflen);
    133            } else {
    134                buf = (unsigned char *)PORT_ArenaGrow(p7dcx->cinfo->poolp,
    135                                                      plain->data,
    136                                                      oldlen, oldlen + buflen);
    137                if (buf != NULL)
    138                    buf += oldlen;
    139            }
    140            plain->data = buf;
    141        }
    142        if (buf == NULL) {
    143            p7dcx->error = SEC_ERROR_NO_MEMORY;
    144            return; /* XXX indicate error? */
    145        }
    146        rv = sec_PKCS7Decrypt(worker->decryptobj, buf, &outlen, buflen,
    147                              data, inlen, final);
    148        if (rv != SECSuccess) {
    149            p7dcx->error = PORT_GetError();
    150            goto cleanup; /* XXX indicate error? */
    151        }
    152        if (plain != NULL) {
    153            PORT_Assert(final || outlen == buflen);
    154            plain->len += outlen;
    155        }
    156        data = buf;
    157        len = outlen;
    158    }
    159 
    160    /*
    161     * Update the running digests.
    162     */
    163    if (len) {
    164        for (i = 0; i < worker->digcnt; i++) {
    165            (*worker->digobjs[i]->update)(worker->digcxs[i], data, len);
    166        }
    167    }
    168 
    169    /*
    170     * Pass back the contents bytes, and free the temporary buffer.
    171     */
    172    if (p7dcx->cb != NULL) {
    173        if (len)
    174            (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
    175    }
    176 
    177 cleanup:
    178    if (freeBuf && buf != NULL) {
    179        PORT_Free(buf);
    180    }
    181 }
    182 
    183 static void
    184 sec_pkcs7_decoder_filter(void *arg, const char *data, unsigned long len,
    185                         int depth, SEC_ASN1EncodingPart data_kind)
    186 {
    187    SEC_PKCS7DecoderContext *p7dcx;
    188    struct sec_pkcs7_decoder_worker *worker;
    189 
    190    /*
    191     * Since we do not handle any nested contents, the only bytes we
    192     * are really interested in are the actual contents bytes (not
    193     * the identifier, length, or end-of-contents bytes).  If we were
    194     * handling nested types we would probably need to do something
    195     * smarter based on depth and data_kind.
    196     */
    197    if (data_kind != SEC_ASN1_Contents)
    198        return;
    199 
    200    /*
    201     * The ASN.1 decoder should not even call us with a length of 0.
    202     * Just being paranoid.
    203     */
    204    PORT_Assert(len);
    205    if (len == 0)
    206        return;
    207 
    208    p7dcx = (SEC_PKCS7DecoderContext *)arg;
    209 
    210    /*
    211     * Handling nested contents would mean that there is a chain
    212     * of workers -- one per each level of content.  The following
    213     * would start with the first worker and loop over them.
    214     */
    215    worker = &(p7dcx->worker);
    216 
    217    worker->saw_contents = PR_TRUE;
    218 
    219    sec_pkcs7_decoder_work_data(p7dcx, worker,
    220                                (const unsigned char *)data, len, PR_FALSE);
    221 }
    222 
    223 /*
    224 * Create digest contexts for each algorithm in "digestalgs".
    225 * No algorithms is not an error, we just do not do anything.
    226 * An error (like trouble allocating memory), marks the error
    227 * in "p7dcx" and returns SECFailure, which means that our caller
    228 * should just give up altogether.
    229 */
    230 static SECStatus
    231 sec_pkcs7_decoder_start_digests(SEC_PKCS7DecoderContext *p7dcx, int depth,
    232                                SECAlgorithmID **digestalgs)
    233 {
    234    int i, digcnt;
    235 
    236    p7dcx->worker.digcnt = 0;
    237 
    238    if (digestalgs == NULL)
    239        return SECSuccess;
    240 
    241    /*
    242     * Count the algorithms.
    243     */
    244    digcnt = 0;
    245    while (digestalgs[digcnt] != NULL)
    246        digcnt++;
    247 
    248    /*
    249     * No algorithms means no work to do.
    250     * Just act as if there were no algorithms specified.
    251     */
    252    if (digcnt == 0)
    253        return SECSuccess;
    254 
    255    p7dcx->worker.digcxs = (void **)PORT_ArenaAlloc(p7dcx->tmp_poolp,
    256                                                    digcnt * sizeof(void *));
    257    p7dcx->worker.digobjs = (const SECHashObject **)PORT_ArenaAlloc(p7dcx->tmp_poolp,
    258                                                                    digcnt * sizeof(SECHashObject *));
    259    if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) {
    260        p7dcx->error = SEC_ERROR_NO_MEMORY;
    261        return SECFailure;
    262    }
    263 
    264    p7dcx->worker.depth = depth;
    265 
    266    /*
    267     * Create a digest context for each algorithm.
    268     */
    269    for (i = 0; i < digcnt; i++) {
    270        SECAlgorithmID *algid = digestalgs[i];
    271        SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm));
    272        const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
    273        void *digcx;
    274 
    275        /*
    276         * Skip any algorithm we do not even recognize; obviously,
    277         * this could be a problem, but if it is critical then the
    278         * result will just be that the signature does not verify.
    279         * We do not necessarily want to error out here, because
    280         * the particular algorithm may not actually be important,
    281         * but we cannot know that until later.
    282         */
    283        if (digobj == NULL) {
    284            continue;
    285        }
    286 
    287        digcx = (*digobj->create)();
    288        if (digcx != NULL) {
    289            (*digobj->begin)(digcx);
    290            p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj;
    291            p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx;
    292            p7dcx->worker.digcnt++;
    293        }
    294    }
    295 
    296    if (p7dcx->worker.digcnt != 0)
    297        SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
    298                                     sec_pkcs7_decoder_filter,
    299                                     p7dcx,
    300                                     (PRBool)(p7dcx->cb != NULL));
    301    return SECSuccess;
    302 }
    303 
    304 /* destroy any active digest contexts without harvesting results */
    305 static void
    306 sec_pkcs7_decoder_abort_digests(struct sec_pkcs7_decoder_worker *worker)
    307 {
    308    int i;
    309 
    310    if (!worker || worker->digcnt <= 0 || !worker->digcxs || !worker->digobjs) {
    311        worker->digcnt = 0;
    312        return;
    313    }
    314 
    315    for (i = 0; i < worker->digcnt; i++) {
    316        if (worker->digcxs[i] && worker->digobjs[i]) {
    317            (*worker->digobjs[i]->destroy)(worker->digcxs[i], PR_TRUE);
    318        }
    319        worker->digcxs[i] = NULL;
    320    }
    321 
    322    worker->digcnt = 0;
    323 }
    324 
    325 /*
    326 * Close out all of the digest contexts, storing the results in "digestsp".
    327 */
    328 static SECStatus
    329 sec_pkcs7_decoder_finish_digests(SEC_PKCS7DecoderContext *p7dcx,
    330                                 PLArenaPool *poolp,
    331                                 SECItem ***digestsp)
    332 {
    333    /*
    334     * XXX Handling nested contents would mean that there is a chain
    335     * of workers -- one per each level of content.  The following
    336     * would want to find the last worker in the chain.
    337     */
    338    struct sec_pkcs7_decoder_worker *worker = &(p7dcx->worker);
    339 
    340    /*
    341     * If no digests, then we have nothing to do.
    342     */
    343    if (worker->digcnt == 0) {
    344        return SECSuccess;
    345    }
    346 
    347    /*
    348     * No matter what happens after this, we want to stop filtering.
    349     * XXX If we handle nested contents, we only want to stop filtering
    350     * if we are finishing off the *last* worker.
    351     */
    352    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
    353 
    354    /*
    355     * If we ended up with no contents, just destroy each
    356     * digest context -- they are meaningless and potentially
    357     * confusing, because their presence would imply some content
    358     * was digested.
    359     */
    360    if (!worker->saw_contents) {
    361        sec_pkcs7_decoder_abort_digests(worker);
    362        return SECSuccess;
    363    }
    364 
    365    void *mark = PORT_ArenaMark(poolp);
    366 
    367    /*
    368     * Close out each digest context, saving digest away.
    369     */
    370    SECItem **digests =
    371        (SECItem **)PORT_ArenaZAlloc(poolp, (worker->digcnt + 1) * sizeof(SECItem *));
    372    if (digests == NULL) {
    373        p7dcx->error = PORT_GetError();
    374        sec_pkcs7_decoder_abort_digests(worker);
    375        PORT_ArenaRelease(poolp, mark);
    376        return SECFailure;
    377    }
    378 
    379    for (int i = 0; i < worker->digcnt; i++) {
    380        const SECHashObject *digobj = worker->digobjs[i];
    381        digests[i] = SECITEM_AllocItem(poolp, NULL, digobj->length);
    382        if (!digests[i]) {
    383            p7dcx->error = PORT_GetError();
    384            sec_pkcs7_decoder_abort_digests(worker);
    385            PORT_ArenaRelease(poolp, mark);
    386            return SECFailure;
    387        }
    388    }
    389 
    390    for (int i = 0; i < worker->digcnt; i++) {
    391        void *digcx = worker->digcxs[i];
    392        const SECHashObject *digobj = worker->digobjs[i];
    393 
    394        (*digobj->end)(digcx, digests[i]->data, &(digests[i]->len), digests[i]->len);
    395        (*digobj->destroy)(digcx, PR_TRUE);
    396        worker->digcxs[i] = NULL;
    397    }
    398    worker->digcnt = 0;
    399    *digestsp = digests;
    400 
    401    PORT_ArenaUnmark(poolp, mark);
    402    return SECSuccess;
    403 }
    404 
    405 /*
    406 * XXX Need comment explaining following helper function (which is used
    407 * by sec_pkcs7_decoder_start_decrypt).
    408 */
    409 
    410 static PK11SymKey *
    411 sec_pkcs7_decoder_get_recipient_key(SEC_PKCS7DecoderContext *p7dcx,
    412                                    SEC_PKCS7RecipientInfo **recipientinfos,
    413                                    SEC_PKCS7EncryptedContentInfo *enccinfo)
    414 {
    415    SEC_PKCS7RecipientInfo *ri;
    416    CERTCertificate *cert = NULL;
    417    SECKEYPrivateKey *privkey = NULL;
    418    PK11SymKey *bulkkey = NULL;
    419    SECOidTag keyalgtag, bulkalgtag, encalgtag;
    420    PK11SlotInfo *slot = NULL;
    421 
    422    if (recipientinfos == NULL || recipientinfos[0] == NULL) {
    423        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
    424        goto no_key_found;
    425    }
    426 
    427    cert = PK11_FindCertAndKeyByRecipientList(&slot, recipientinfos, &ri,
    428                                              &privkey, p7dcx->pwfn_arg);
    429    if (cert == NULL) {
    430        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
    431        goto no_key_found;
    432    }
    433 
    434    ri->cert = cert; /* so we can find it later */
    435    PORT_Assert(privkey != NULL);
    436 
    437    keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
    438    encalgtag = SECOID_GetAlgorithmTag(&(ri->keyEncAlg));
    439    if (keyalgtag != encalgtag) {
    440        p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH;
    441        goto no_key_found;
    442    }
    443    bulkalgtag = SECOID_GetAlgorithmTag(&(enccinfo->contentEncAlg));
    444 
    445    switch (encalgtag) {
    446        case SEC_OID_PKCS1_RSA_ENCRYPTION:
    447            bulkkey = PK11_PubUnwrapSymKey(privkey, &ri->encKey,
    448                                           PK11_AlgtagToMechanism(bulkalgtag),
    449                                           CKA_DECRYPT, 0);
    450            if (bulkkey == NULL) {
    451                p7dcx->error = PORT_GetError();
    452                PORT_SetError(0);
    453                goto no_key_found;
    454            }
    455            break;
    456        default:
    457            p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
    458            break;
    459    }
    460 
    461 no_key_found:
    462    if (privkey != NULL)
    463        SECKEY_DestroyPrivateKey(privkey);
    464    if (slot != NULL)
    465        PK11_FreeSlot(slot);
    466 
    467    return bulkkey;
    468 }
    469 
    470 /*
    471 * XXX The following comment is old -- the function used to only handle
    472 * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
    473 * as well (and it had all of the code of the helper function above
    474 * built into it), though the comment was left as is.  Fix it...
    475 *
    476 * We are just about to decode the content of an EnvelopedData.
    477 * Set up a decryption context so we can decrypt as we go.
    478 * Presumably we are one of the recipients listed in "recipientinfos".
    479 * (XXX And if we are not, or if we have trouble, what should we do?
    480 *  It would be nice to let the decoding still work.  Maybe it should
    481 *  be an error if there is a content callback, but not an error otherwise?)
    482 * The encryption key and related information can be found in "enccinfo".
    483 */
    484 static SECStatus
    485 sec_pkcs7_decoder_start_decrypt(SEC_PKCS7DecoderContext *p7dcx, int depth,
    486                                SEC_PKCS7RecipientInfo **recipientinfos,
    487                                SEC_PKCS7EncryptedContentInfo *enccinfo,
    488                                PK11SymKey **copy_key_for_signature)
    489 {
    490    PK11SymKey *bulkkey = NULL;
    491    sec_PKCS7CipherObject *decryptobj;
    492 
    493    /*
    494     * If a callback is supplied to retrieve the encryption key,
    495     * for instance, for Encrypted Content infos, then retrieve
    496     * the bulkkey from the callback.  Otherwise, assume that
    497     * we are processing Enveloped or SignedAndEnveloped data
    498     * content infos.
    499     *
    500     * XXX Put an assert here?
    501     */
    502    if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
    503        if (p7dcx->dkcb != NULL) {
    504            bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg,
    505                                     &(enccinfo->contentEncAlg));
    506        }
    507        enccinfo->keysize = 0;
    508    } else {
    509        bulkkey = sec_pkcs7_decoder_get_recipient_key(p7dcx, recipientinfos,
    510                                                      enccinfo);
    511        if (bulkkey == NULL)
    512            goto no_decryption;
    513        enccinfo->keysize = PK11_GetKeyStrength(bulkkey,
    514                                                &(enccinfo->contentEncAlg));
    515    }
    516 
    517    /*
    518     * XXX I think following should set error in p7dcx and clear set error
    519     * (as used to be done here, or as is done in get_receipient_key above.
    520     */
    521    if (bulkkey == NULL) {
    522        goto no_decryption;
    523    }
    524 
    525    /*
    526     * We want to make sure decryption is allowed.  This is done via
    527     * a callback specified in SEC_PKCS7DecoderStart().
    528     */
    529    if (p7dcx->decrypt_allowed_cb) {
    530        if ((*p7dcx->decrypt_allowed_cb)(&(enccinfo->contentEncAlg),
    531                                         bulkkey) == PR_FALSE) {
    532            p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
    533            goto no_decryption;
    534        }
    535    } else {
    536        p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
    537        goto no_decryption;
    538    }
    539 
    540    /*
    541     * When decrypting a signedAndEnvelopedData, the signature also has
    542     * to be decrypted with the bulk encryption key; to avoid having to
    543     * get it all over again later (and do another potentially expensive
    544     * RSA operation), copy it for later signature verification to use.
    545     */
    546    if (copy_key_for_signature != NULL)
    547        *copy_key_for_signature = PK11_ReferenceSymKey(bulkkey);
    548 
    549    /*
    550     * Now we have the bulk encryption key (in bulkkey) and the
    551     * the algorithm (in enccinfo->contentEncAlg).  Using those,
    552     * create a decryption context.
    553     */
    554    decryptobj = sec_PKCS7CreateDecryptObject(bulkkey,
    555                                              &(enccinfo->contentEncAlg));
    556 
    557    /*
    558     * We are done with (this) bulkkey now.
    559     */
    560    PK11_FreeSymKey(bulkkey);
    561    bulkkey = NULL;
    562 
    563    if (decryptobj == NULL) {
    564        p7dcx->error = PORT_GetError();
    565        PORT_SetError(0);
    566        goto no_decryption;
    567    }
    568 
    569    SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
    570                                 sec_pkcs7_decoder_filter,
    571                                 p7dcx,
    572                                 (PRBool)(p7dcx->cb != NULL));
    573 
    574    p7dcx->worker.depth = depth;
    575    p7dcx->worker.decryptobj = decryptobj;
    576 
    577    return SECSuccess;
    578 
    579 no_decryption:
    580    PK11_FreeSymKey(bulkkey);
    581    /*
    582     * For some reason (error set already, if appropriate), we cannot
    583     * decrypt the content.  I am not sure what exactly is the right
    584     * thing to do here; in some cases we want to just stop, and in
    585     * others we want to let the decoding finish even though we cannot
    586     * decrypt the content.  My current thinking is that if the caller
    587     * set up a content callback, then they are really interested in
    588     * getting (decrypted) content, and if they cannot they will want
    589     * to know about it.  However, if no callback was specified, then
    590     * maybe it is not important that the decryption failed.
    591     */
    592    if (p7dcx->cb != NULL)
    593        return SECFailure;
    594    else
    595        return SECSuccess; /* Let the decoding continue. */
    596 }
    597 
    598 static SECStatus
    599 sec_pkcs7_decoder_finish_decrypt(SEC_PKCS7DecoderContext *p7dcx,
    600                                 PLArenaPool *poolp,
    601                                 SEC_PKCS7EncryptedContentInfo *enccinfo)
    602 {
    603    struct sec_pkcs7_decoder_worker *worker;
    604 
    605    /*
    606     * XXX Handling nested contents would mean that there is a chain
    607     * of workers -- one per each level of content.  The following
    608     * would want to find the last worker in the chain.
    609     */
    610    worker = &(p7dcx->worker);
    611 
    612    /*
    613     * If no decryption context, then we have nothing to do.
    614     */
    615    if (worker->decryptobj == NULL)
    616        return SECSuccess;
    617 
    618    /*
    619     * No matter what happens after this, we want to stop filtering.
    620     * XXX If we handle nested contents, we only want to stop filtering
    621     * if we are finishing off the *last* worker.
    622     */
    623    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
    624 
    625    /*
    626     * Handle the last block.
    627     */
    628    sec_pkcs7_decoder_work_data(p7dcx, worker, NULL, 0, PR_TRUE);
    629 
    630    /*
    631     * The callback invoked from work_data may have aborted and already
    632     * torn down the decrypt context, so only destroy if it is still set.
    633     */
    634    if (worker->decryptobj) {
    635        sec_PKCS7DestroyDecryptObject(worker->decryptobj);
    636        worker->decryptobj = NULL;
    637    }
    638 
    639    return SECSuccess;
    640 }
    641 
    642 static void
    643 sec_pkcs7_decoder_notify(void *arg, PRBool before, void *dest, int depth)
    644 {
    645    SEC_PKCS7DecoderContext *p7dcx;
    646    SEC_PKCS7ContentInfo *cinfo;
    647    SEC_PKCS7SignedData *sigd;
    648    SEC_PKCS7EnvelopedData *envd;
    649    SEC_PKCS7SignedAndEnvelopedData *saed;
    650    SEC_PKCS7EncryptedData *encd;
    651    SEC_PKCS7DigestedData *digd;
    652    PRBool after;
    653    SECStatus rv;
    654 
    655    /*
    656     * Just to make the code easier to read, create an "after" variable
    657     * that is equivalent to "not before".
    658     * (This used to be just the statement "after = !before", but that
    659     * causes a warning on the mac; to avoid that, we do it the long way.)
    660     */
    661    if (before)
    662        after = PR_FALSE;
    663    else
    664        after = PR_TRUE;
    665 
    666    p7dcx = (SEC_PKCS7DecoderContext *)arg;
    667    if (!p7dcx) {
    668        return;
    669    }
    670 
    671    cinfo = p7dcx->cinfo;
    672 
    673    if (!cinfo) {
    674        return;
    675    }
    676 
    677    if (cinfo->contentTypeTag == NULL) {
    678        if (after && dest == &(cinfo->contentType))
    679            cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
    680        return;
    681    }
    682 
    683    switch (cinfo->contentTypeTag->offset) {
    684        case SEC_OID_PKCS7_SIGNED_DATA:
    685            sigd = cinfo->content.signedData;
    686            if (sigd == NULL)
    687                break;
    688 
    689            if (sigd->contentInfo.contentTypeTag == NULL) {
    690                if (after && dest == &(sigd->contentInfo.contentType))
    691                    sigd->contentInfo.contentTypeTag =
    692                        SECOID_FindOID(&(sigd->contentInfo.contentType));
    693                break;
    694            }
    695 
    696            /*
    697             * We only set up a filtering digest if the content is
    698             * plain DATA; anything else needs more work because a
    699             * second pass is required to produce a DER encoding from
    700             * an input that can be BER encoded.  (This is a requirement
    701             * of PKCS7 that is unfortunate, but there you have it.)
    702             *
    703             * XXX Also, since we stop here if this is not DATA, the
    704             * inner content is not getting processed at all.  Someday
    705             * we may want to fix that.
    706             */
    707            if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
    708                /* XXX Set an error in p7dcx->error */
    709                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    710                break;
    711            }
    712 
    713            /*
    714             * Just before the content, we want to set up a digest context
    715             * for each digest algorithm listed, and start a filter which
    716             * will run all of the contents bytes through that digest.
    717             */
    718            if (before && dest == &(sigd->contentInfo.content)) {
    719                rv = sec_pkcs7_decoder_start_digests(p7dcx, depth,
    720                                                     sigd->digestAlgorithms);
    721                if (rv != SECSuccess)
    722                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    723 
    724                break;
    725            }
    726 
    727            /*
    728             * XXX To handle nested types, here is where we would want
    729             * to check for inner boundaries that need handling.
    730             */
    731 
    732            /*
    733             * Are we done?
    734             */
    735            if (after && dest == &(sigd->contentInfo.content)) {
    736                /*
    737                 * Close out the digest contexts.  We ignore any error
    738                 * because we are stopping anyway; the error status left
    739                 * behind in p7dcx will be seen by outer functions.
    740                 */
    741                (void)sec_pkcs7_decoder_finish_digests(p7dcx, cinfo->poolp,
    742                                                       &(sigd->digests));
    743 
    744                /*
    745                 * XXX To handle nested contents, we would need to remove
    746                 * the worker from the chain (and free it).
    747                 */
    748 
    749                /*
    750                 * Stop notify.
    751                 */
    752                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    753            }
    754            break;
    755 
    756        case SEC_OID_PKCS7_ENVELOPED_DATA:
    757            envd = cinfo->content.envelopedData;
    758            if (envd == NULL)
    759                break;
    760 
    761            if (envd->encContentInfo.contentTypeTag == NULL) {
    762                if (after && dest == &(envd->encContentInfo.contentType))
    763                    envd->encContentInfo.contentTypeTag =
    764                        SECOID_FindOID(&(envd->encContentInfo.contentType));
    765                break;
    766            }
    767 
    768            /*
    769             * Just before the content, we want to set up a decryption
    770             * context, and start a filter which will run all of the
    771             * contents bytes through it to determine the plain content.
    772             */
    773            if (before && dest == &(envd->encContentInfo.encContent)) {
    774                rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth,
    775                                                     envd->recipientInfos,
    776                                                     &(envd->encContentInfo),
    777                                                     NULL);
    778                if (rv != SECSuccess)
    779                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    780 
    781                break;
    782            }
    783 
    784            /*
    785             * Are we done?
    786             */
    787            if (after && dest == &(envd->encContentInfo.encContent)) {
    788                /*
    789                 * Close out the decryption context.  We ignore any error
    790                 * because we are stopping anyway; the error status left
    791                 * behind in p7dcx will be seen by outer functions.
    792                 */
    793                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
    794                                                       &(envd->encContentInfo));
    795 
    796                /*
    797                 * XXX To handle nested contents, we would need to remove
    798                 * the worker from the chain (and free it).
    799                 */
    800 
    801                /*
    802                 * Stop notify.
    803                 */
    804                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    805            }
    806            break;
    807 
    808        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    809            saed = cinfo->content.signedAndEnvelopedData;
    810            if (saed == NULL)
    811                break;
    812 
    813            if (saed->encContentInfo.contentTypeTag == NULL) {
    814                if (after && dest == &(saed->encContentInfo.contentType))
    815                    saed->encContentInfo.contentTypeTag =
    816                        SECOID_FindOID(&(saed->encContentInfo.contentType));
    817                break;
    818            }
    819 
    820            /*
    821             * Just before the content, we want to set up a decryption
    822             * context *and* digest contexts, and start a filter which
    823             * will run all of the contents bytes through both.
    824             */
    825            if (before && dest == &(saed->encContentInfo.encContent)) {
    826                rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth,
    827                                                     saed->recipientInfos,
    828                                                     &(saed->encContentInfo),
    829                                                     &(saed->sigKey));
    830                if (rv == SECSuccess)
    831                    rv = sec_pkcs7_decoder_start_digests(p7dcx, depth,
    832                                                         saed->digestAlgorithms);
    833                if (rv != SECSuccess)
    834                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    835 
    836                break;
    837            }
    838 
    839            /*
    840             * Are we done?
    841             */
    842            if (after && dest == &(saed->encContentInfo.encContent)) {
    843                /*
    844                 * Close out the decryption and digests contexts.
    845                 * We ignore any errors because we are stopping anyway;
    846                 * the error status left behind in p7dcx will be seen by
    847                 * outer functions.
    848                 *
    849                 * Note that the decrypt stuff must be called first;
    850                 * it may have a last buffer to do which in turn has
    851                 * to be added to the digest.
    852                 */
    853                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
    854                                                       &(saed->encContentInfo));
    855                (void)sec_pkcs7_decoder_finish_digests(p7dcx, cinfo->poolp,
    856                                                       &(saed->digests));
    857 
    858                /*
    859                 * XXX To handle nested contents, we would need to remove
    860                 * the worker from the chain (and free it).
    861                 */
    862 
    863                /*
    864                 * Stop notify.
    865                 */
    866                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    867            }
    868            break;
    869 
    870        case SEC_OID_PKCS7_DIGESTED_DATA:
    871            digd = cinfo->content.digestedData;
    872            if (digd == NULL)
    873                break;
    874 
    875            /*
    876             * XXX Want to do the digest or not?  Maybe future enhancement...
    877             */
    878            if (before && dest == &(digd->contentInfo.content.data)) {
    879                SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, sec_pkcs7_decoder_filter,
    880                                             p7dcx,
    881                                             (PRBool)(p7dcx->cb != NULL));
    882                break;
    883            }
    884 
    885            /*
    886             * Are we done?
    887             */
    888            if (after && dest == &(digd->contentInfo.content.data)) {
    889                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
    890            }
    891            break;
    892 
    893        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    894            encd = cinfo->content.encryptedData;
    895 
    896            if (!encd) {
    897                break;
    898            }
    899 
    900            /*
    901             * XXX If the decryption key callback is set, we want to start
    902             * the decryption.  If the callback is not set, we will treat the
    903             * content as plain data, since we do not have the key.
    904             *
    905             * Is this the proper thing to do?
    906             */
    907            if (before && dest == &(encd->encContentInfo.encContent)) {
    908                /*
    909                 * Start the encryption process if the decryption key callback
    910                 * is present.  Otherwise, treat the content like plain data.
    911                 */
    912                rv = SECSuccess;
    913                if (p7dcx->dkcb != NULL) {
    914                    rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth, NULL,
    915                                                         &(encd->encContentInfo),
    916                                                         NULL);
    917                }
    918 
    919                if (rv != SECSuccess)
    920                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    921 
    922                break;
    923            }
    924 
    925            /*
    926             * Are we done?
    927             */
    928            if (after && dest == &(encd->encContentInfo.encContent)) {
    929                /*
    930                 * Close out the decryption context.  We ignore any error
    931                 * because we are stopping anyway; the error status left
    932                 * behind in p7dcx will be seen by outer functions.
    933                 */
    934                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
    935                                                       &(encd->encContentInfo));
    936 
    937                /*
    938                 * Stop notify.
    939                 */
    940                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    941            }
    942            break;
    943 
    944        case SEC_OID_PKCS7_DATA:
    945            /*
    946             * If a output callback has been specified, we want to set the filter
    947             * to call the callback.  This is taken care of in
    948             * sec_pkcs7_decoder_start_decrypt() or
    949             * sec_pkcs7_decoder_start_digests() for the other content types.
    950             */
    951 
    952            if (before && dest == &(cinfo->content.data)) {
    953 
    954                /*
    955                 * Set the filter proc up.
    956                 */
    957                SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
    958                                             sec_pkcs7_decoder_filter,
    959                                             p7dcx,
    960                                             (PRBool)(p7dcx->cb != NULL));
    961                break;
    962            }
    963 
    964            if (after && dest == &(cinfo->content.data)) {
    965                /*
    966                 * Time to clean up after ourself, stop the Notify and Filter
    967                 * procedures.
    968                 */
    969                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    970                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
    971            }
    972            break;
    973 
    974        default:
    975            SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
    976            break;
    977    }
    978 }
    979 
    980 SEC_PKCS7DecoderContext *
    981 SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
    982                      SECKEYGetPasswordKey pwfn, void *pwfn_arg,
    983                      SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
    984                      void *decrypt_key_cb_arg,
    985                      SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
    986 {
    987    SEC_PKCS7DecoderContext *p7dcx;
    988    SEC_ASN1DecoderContext *dcx;
    989    SEC_PKCS7ContentInfo *cinfo;
    990    PLArenaPool *poolp;
    991 
    992    poolp = PORT_NewArena(1024); /* XXX what is right value? */
    993    if (poolp == NULL)
    994        return NULL;
    995 
    996    cinfo = (SEC_PKCS7ContentInfo *)PORT_ArenaZAlloc(poolp, sizeof(*cinfo));
    997    if (cinfo == NULL) {
    998        PORT_FreeArena(poolp, PR_FALSE);
    999        return NULL;
   1000    }
   1001 
   1002    cinfo->poolp = poolp;
   1003    cinfo->pwfn = pwfn;
   1004    cinfo->pwfn_arg = pwfn_arg;
   1005    cinfo->created = PR_FALSE;
   1006    cinfo->refCount = 1;
   1007 
   1008    p7dcx =
   1009        (SEC_PKCS7DecoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7DecoderContext));
   1010    if (p7dcx == NULL) {
   1011        PORT_FreeArena(poolp, PR_FALSE);
   1012        return NULL;
   1013    }
   1014 
   1015    p7dcx->tmp_poolp = PORT_NewArena(1024); /* XXX what is right value? */
   1016    if (p7dcx->tmp_poolp == NULL) {
   1017        PORT_Free(p7dcx);
   1018        PORT_FreeArena(poolp, PR_FALSE);
   1019        return NULL;
   1020    }
   1021 
   1022    dcx = SEC_ASN1DecoderStart(poolp, cinfo, sec_PKCS7ContentInfoTemplate);
   1023    if (dcx == NULL) {
   1024        PORT_FreeArena(p7dcx->tmp_poolp, PR_FALSE);
   1025        PORT_Free(p7dcx);
   1026        PORT_FreeArena(poolp, PR_FALSE);
   1027        return NULL;
   1028    }
   1029 
   1030    SEC_ASN1DecoderSetNotifyProc(dcx, sec_pkcs7_decoder_notify, p7dcx);
   1031 
   1032    p7dcx->dcx = dcx;
   1033    p7dcx->cinfo = cinfo;
   1034    p7dcx->cb = cb;
   1035    p7dcx->cb_arg = cb_arg;
   1036    p7dcx->pwfn = pwfn;
   1037    p7dcx->pwfn_arg = pwfn_arg;
   1038    p7dcx->dkcb = decrypt_key_cb;
   1039    p7dcx->dkcb_arg = decrypt_key_cb_arg;
   1040    p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;
   1041 
   1042    return p7dcx;
   1043 }
   1044 
   1045 /*
   1046 * Do the next chunk of PKCS7 decoding.  If there is a problem, set
   1047 * an error and return a failure status.  Note that in the case of
   1048 * an error, this routine is still prepared to be called again and
   1049 * again in case that is the easiest route for our caller to take.
   1050 * We simply detect it and do not do anything except keep setting
   1051 * that error in case our caller has not noticed it yet...
   1052 */
   1053 SECStatus
   1054 SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
   1055                       const char *buf, unsigned long len)
   1056 {
   1057    if (!p7dcx) {
   1058        PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1059        return SECFailure;
   1060    }
   1061 
   1062    if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) {
   1063        PORT_Assert(p7dcx->error == 0);
   1064        if (p7dcx->error == 0) {
   1065            if (SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len) != SECSuccess) {
   1066                p7dcx->error = PORT_GetError();
   1067                PORT_Assert(p7dcx->error);
   1068                if (p7dcx->error == 0)
   1069                    p7dcx->error = -1;
   1070            }
   1071        }
   1072    }
   1073 
   1074    if (p7dcx->error) {
   1075        sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
   1076        if (p7dcx->worker.decryptobj) {
   1077            sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
   1078            p7dcx->worker.decryptobj = NULL;
   1079        }
   1080        if (p7dcx->dcx != NULL) {
   1081            (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
   1082            p7dcx->dcx = NULL;
   1083        }
   1084        if (p7dcx->cinfo != NULL) {
   1085            SEC_PKCS7DestroyContentInfo(p7dcx->cinfo);
   1086            p7dcx->cinfo = NULL;
   1087        }
   1088        PORT_SetError(p7dcx->error);
   1089        return SECFailure;
   1090    }
   1091 
   1092    return SECSuccess;
   1093 }
   1094 
   1095 SEC_PKCS7ContentInfo *
   1096 SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx)
   1097 {
   1098    SEC_PKCS7ContentInfo *cinfo;
   1099 
   1100    sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
   1101    cinfo = p7dcx->cinfo;
   1102    if (p7dcx->dcx != NULL) {
   1103        if (SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess) {
   1104            SEC_PKCS7DestroyContentInfo(cinfo);
   1105            cinfo = NULL;
   1106        }
   1107    }
   1108    /* free any NSS data structures */
   1109    if (p7dcx->worker.decryptobj) {
   1110        sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
   1111        p7dcx->worker.decryptobj = NULL;
   1112    }
   1113 
   1114    PORT_FreeArena(p7dcx->tmp_poolp, PR_FALSE);
   1115    PORT_Free(p7dcx);
   1116    return cinfo;
   1117 }
   1118 
   1119 SEC_PKCS7ContentInfo *
   1120 SEC_PKCS7DecodeItem(SECItem *p7item,
   1121                    SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
   1122                    SECKEYGetPasswordKey pwfn, void *pwfn_arg,
   1123                    SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
   1124                    void *decrypt_key_cb_arg,
   1125                    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
   1126 {
   1127    SEC_PKCS7DecoderContext *p7dcx;
   1128 
   1129    p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,
   1130                                  decrypt_key_cb_arg, decrypt_allowed_cb);
   1131    if (!p7dcx) {
   1132        /* error code is set */
   1133        return NULL;
   1134    }
   1135    (void)SEC_PKCS7DecoderUpdate(p7dcx, (char *)p7item->data, p7item->len);
   1136    return SEC_PKCS7DecoderFinish(p7dcx);
   1137 }
   1138 
   1139 /*
   1140 * Abort the ASN.1 stream. Used by pkcs 12
   1141 */
   1142 void
   1143 SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error)
   1144 {
   1145    PORT_Assert(p7dcx);
   1146    if (!p7dcx) {
   1147        return;
   1148    }
   1149 
   1150    /* ensure any streaming helpers are torn down */
   1151    sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
   1152    if (p7dcx->worker.decryptobj) {
   1153        sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
   1154        p7dcx->worker.decryptobj = NULL;
   1155    }
   1156 
   1157    SEC_ASN1DecoderAbort(p7dcx->dcx, error);
   1158 }
   1159 
   1160 /*
   1161 * If the thing contains any certs or crls return true; false otherwise.
   1162 */
   1163 PRBool
   1164 SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo)
   1165 {
   1166    SECOidTag kind;
   1167    SECItem **certs;
   1168    CERTSignedCrl **crls;
   1169 
   1170    kind = SEC_PKCS7ContentType(cinfo);
   1171    switch (kind) {
   1172        default:
   1173        case SEC_OID_PKCS7_DATA:
   1174        case SEC_OID_PKCS7_DIGESTED_DATA:
   1175        case SEC_OID_PKCS7_ENVELOPED_DATA:
   1176        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1177            return PR_FALSE;
   1178        case SEC_OID_PKCS7_SIGNED_DATA:
   1179            certs = cinfo->content.signedData->rawCerts;
   1180            crls = cinfo->content.signedData->crls;
   1181            break;
   1182        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   1183            certs = cinfo->content.signedAndEnvelopedData->rawCerts;
   1184            crls = cinfo->content.signedAndEnvelopedData->crls;
   1185            break;
   1186    }
   1187 
   1188    /*
   1189     * I know this could be collapsed, but I was in a mood to be explicit.
   1190     */
   1191    if (certs != NULL && certs[0] != NULL)
   1192        return PR_TRUE;
   1193    else if (crls != NULL && crls[0] != NULL)
   1194        return PR_TRUE;
   1195    else
   1196        return PR_FALSE;
   1197 }
   1198 
   1199 /* return the content length...could use GetContent, however we
   1200 * need the encrypted content length
   1201 */
   1202 PRBool
   1203 SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen)
   1204 {
   1205    SECItem *item = NULL;
   1206 
   1207    if (cinfo == NULL) {
   1208        return PR_TRUE;
   1209    }
   1210 
   1211    switch (SEC_PKCS7ContentType(cinfo)) {
   1212        case SEC_OID_PKCS7_DATA:
   1213            item = cinfo->content.data;
   1214            break;
   1215        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1216            item = &cinfo->content.encryptedData->encContentInfo.encContent;
   1217            break;
   1218        default:
   1219            /* add other types */
   1220            return PR_FALSE;
   1221    }
   1222 
   1223    if (!item) {
   1224        return PR_TRUE;
   1225    } else if (item->len <= minLen) {
   1226        return PR_TRUE;
   1227    }
   1228 
   1229    return PR_FALSE;
   1230 }
   1231 
   1232 PRBool
   1233 SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo)
   1234 {
   1235    SECOidTag kind;
   1236 
   1237    kind = SEC_PKCS7ContentType(cinfo);
   1238    switch (kind) {
   1239        default:
   1240        case SEC_OID_PKCS7_DATA:
   1241        case SEC_OID_PKCS7_DIGESTED_DATA:
   1242        case SEC_OID_PKCS7_SIGNED_DATA:
   1243            return PR_FALSE;
   1244        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1245        case SEC_OID_PKCS7_ENVELOPED_DATA:
   1246        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   1247            return PR_TRUE;
   1248    }
   1249 }
   1250 
   1251 /*
   1252 * If the PKCS7 content has a signature (not just *could* have a signature)
   1253 * return true; false otherwise.  This can/should be called before calling
   1254 * VerifySignature, which will always indicate failure if no signature is
   1255 * present, but that does not mean there even was a signature!
   1256 * Note that the content itself can be empty (detached content was sent
   1257 * another way); it is the presence of the signature that matters.
   1258 */
   1259 PRBool
   1260 SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo)
   1261 {
   1262    SECOidTag kind;
   1263    SEC_PKCS7SignerInfo **signerinfos;
   1264 
   1265    kind = SEC_PKCS7ContentType(cinfo);
   1266    switch (kind) {
   1267        default:
   1268        case SEC_OID_PKCS7_DATA:
   1269        case SEC_OID_PKCS7_DIGESTED_DATA:
   1270        case SEC_OID_PKCS7_ENVELOPED_DATA:
   1271        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1272            return PR_FALSE;
   1273        case SEC_OID_PKCS7_SIGNED_DATA:
   1274            signerinfos = cinfo->content.signedData->signerInfos;
   1275            break;
   1276        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   1277            signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
   1278            break;
   1279    }
   1280 
   1281    /*
   1282     * I know this could be collapsed; but I kind of think it will get
   1283     * more complicated before I am finished, so...
   1284     */
   1285    if (signerinfos != NULL && signerinfos[0] != NULL)
   1286        return PR_TRUE;
   1287    else
   1288        return PR_FALSE;
   1289 }
   1290 
   1291 /*
   1292 * sec_pkcs7_verify_signature
   1293 *
   1294 *      Look at a PKCS7 contentInfo and check if the signature is good.
   1295 *      The digest was either calculated earlier (and is stored in the
   1296 *      contentInfo itself) or is passed in via "detached_digest".
   1297 *
   1298 *      The verification checks that the signing cert is valid and trusted
   1299 *      for the purpose specified by "certusage" at
   1300 *      - "*atTime" if "atTime" is not null, or
   1301 *      - the signing time if the signing time is available in "cinfo", or
   1302 *      - the current time (as returned by PR_Now).
   1303 *
   1304 *      In addition, if "keepcerts" is true, add any new certificates found
   1305 *      into our local database.
   1306 *
   1307 * XXX Each place which returns PR_FALSE should be sure to have a good
   1308 * error set for inspection by the caller.  Alternatively, we could create
   1309 * an enumeration of success and each type of failure and return that
   1310 * instead of a boolean.  For now, the default in a bad situation is to
   1311 * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE.  But this should be
   1312 * reviewed; better (more specific) errors should be possible (to distinguish
   1313 * a signature failure from a badly-formed pkcs7 signedData, for example).
   1314 * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE,
   1315 * but that has a less helpful error string associated with it right now;
   1316 * if/when that changes, review and change these as needed.
   1317 *
   1318 * XXX This is broken wrt signedAndEnvelopedData.  In that case, the
   1319 * message digest is doubly encrypted -- first encrypted with the signer
   1320 * private key but then again encrypted with the bulk encryption key used
   1321 * to encrypt the content.  So before we can pass the digest to VerifyDigest,
   1322 * we need to decrypt it with the bulk encryption key.  Also, in this case,
   1323 * there should be NO authenticatedAttributes (signerinfo->authAttr should
   1324 * be NULL).
   1325 */
   1326 static PRBool
   1327 sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
   1328                           SECCertUsage certusage,
   1329                           const SECItem *detached_digest,
   1330                           HASH_HashType digest_type,
   1331                           PRBool keepcerts,
   1332                           const PRTime *atTime)
   1333 {
   1334    SECAlgorithmID **digestalgs, *bulkid;
   1335    const SECItem *digest;
   1336    SECItem **digests;
   1337    SECItem **rawcerts;
   1338    SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
   1339    CERTCertificate *cert, **certs;
   1340    PRBool goodsig;
   1341    CERTCertDBHandle *certdb, *defaultdb;
   1342    SECOidTag encTag, digestTag;
   1343    HASH_HashType found_type;
   1344    int i, certcount;
   1345    SECKEYPublicKey *publickey;
   1346    SECItem *content_type;
   1347    PK11SymKey *sigkey;
   1348    SECItem *encoded_stime;
   1349    PRTime stime;
   1350    PRTime verificationTime;
   1351    SECStatus rv;
   1352 
   1353    /*
   1354     * Everything needed in order to "goto done" safely.
   1355     */
   1356    goodsig = PR_FALSE;
   1357    certcount = 0;
   1358    cert = NULL;
   1359    certs = NULL;
   1360    certdb = NULL;
   1361    defaultdb = CERT_GetDefaultCertDB();
   1362    publickey = NULL;
   1363 
   1364    if (!SEC_PKCS7ContentIsSigned(cinfo)) {
   1365        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1366        goto done;
   1367    }
   1368 
   1369    PORT_Assert(cinfo->contentTypeTag != NULL);
   1370 
   1371    switch (cinfo->contentTypeTag->offset) {
   1372        default:
   1373        case SEC_OID_PKCS7_DATA:
   1374        case SEC_OID_PKCS7_DIGESTED_DATA:
   1375        case SEC_OID_PKCS7_ENVELOPED_DATA:
   1376        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1377            /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */
   1378            PORT_Assert(0);
   1379        case SEC_OID_PKCS7_SIGNED_DATA: {
   1380            SEC_PKCS7SignedData *sdp;
   1381 
   1382            sdp = cinfo->content.signedData;
   1383            digestalgs = sdp->digestAlgorithms;
   1384            digests = sdp->digests;
   1385            rawcerts = sdp->rawCerts;
   1386            signerinfos = sdp->signerInfos;
   1387            content_type = &(sdp->contentInfo.contentType);
   1388            sigkey = NULL;
   1389            bulkid = NULL;
   1390        } break;
   1391        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
   1392            SEC_PKCS7SignedAndEnvelopedData *saedp;
   1393 
   1394            saedp = cinfo->content.signedAndEnvelopedData;
   1395            digestalgs = saedp->digestAlgorithms;
   1396            digests = saedp->digests;
   1397            rawcerts = saedp->rawCerts;
   1398            signerinfos = saedp->signerInfos;
   1399            content_type = &(saedp->encContentInfo.contentType);
   1400            sigkey = saedp->sigKey;
   1401            bulkid = &(saedp->encContentInfo.contentEncAlg);
   1402        } break;
   1403    }
   1404 
   1405    if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {
   1406        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1407        goto done;
   1408    }
   1409 
   1410    /*
   1411     * XXX Need to handle multiple signatures; checking them is easy,
   1412     * but what should be the semantics here (like, return value)?
   1413     */
   1414    if (signerinfos[1] != NULL) {
   1415        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1416        goto done;
   1417    }
   1418 
   1419    signerinfo = signerinfos[0];
   1420 
   1421    /*
   1422     * XXX I would like to just pass the issuerAndSN, along with the rawcerts
   1423     * and crls, to some function that did all of this certificate stuff
   1424     * (open/close the database if necessary, verifying the certs, etc.)
   1425     * and gave me back a cert pointer if all was good.
   1426     */
   1427    certdb = defaultdb;
   1428    if (certdb == NULL) {
   1429        goto done;
   1430    }
   1431 
   1432    certcount = 0;
   1433    if (rawcerts != NULL) {
   1434        for (; rawcerts[certcount] != NULL; certcount++) {
   1435            /* just counting */
   1436        }
   1437    }
   1438 
   1439    /*
   1440     * Note that the result of this is that each cert in "certs"
   1441     * needs to be destroyed.
   1442     */
   1443    rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,
   1444                          keepcerts, PR_FALSE, NULL);
   1445    if (rv != SECSuccess) {
   1446        goto done;
   1447    }
   1448 
   1449    /*
   1450     * This cert will also need to be freed, but since we save it
   1451     * in signerinfo for later, we do not want to destroy it when
   1452     * we leave this function -- we let the clean-up of the entire
   1453     * cinfo structure later do the destroy of this cert.
   1454     */
   1455    cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
   1456    if (cert == NULL) {
   1457        goto done;
   1458    }
   1459 
   1460    signerinfo->cert = cert;
   1461 
   1462    /*
   1463     * Get and convert the signing time; if available, it will be used
   1464     * both on the cert verification and for importing the sender
   1465     * email profile.
   1466     */
   1467    encoded_stime = SEC_PKCS7GetSigningTime(cinfo);
   1468    if (encoded_stime != NULL) {
   1469        if (DER_DecodeTimeChoice(&stime, encoded_stime) != SECSuccess)
   1470            encoded_stime = NULL; /* conversion failed, so pretend none */
   1471    }
   1472 
   1473    /*
   1474     * XXX  This uses the signing time, if available.  Additionally, we
   1475     * might want to, if there is no signing time, get the message time
   1476     * from the mail header itself, and use that.  That would require
   1477     * a change to our interface though, and for S/MIME callers to pass
   1478     * in a time (and for non-S/MIME callers to pass in nothing, or
   1479     * maybe make them pass in the current time, always?).
   1480     */
   1481    if (atTime) {
   1482        verificationTime = *atTime;
   1483    } else if (encoded_stime != NULL) {
   1484        verificationTime = stime;
   1485    } else {
   1486        verificationTime = PR_Now();
   1487    }
   1488    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, verificationTime,
   1489                        cinfo->pwfn_arg, NULL) != SECSuccess) {
   1490        /*
   1491         * XXX Give the user an option to check the signature anyway?
   1492         * If we want to do this, need to give a way to leave and display
   1493         * some dialog and get the answer and come back through (or do
   1494         * the rest of what we do below elsewhere, maybe by putting it
   1495         * in a function that we call below and could call from a dialog
   1496         * finish handler).
   1497         */
   1498        goto savecert;
   1499    }
   1500 
   1501    publickey = CERT_ExtractPublicKey(cert);
   1502    if (publickey == NULL)
   1503        goto done;
   1504 
   1505    /*
   1506     * XXX No!  If digests is empty, see if we can create it now by
   1507     * digesting the contents.  This is necessary if we want to allow
   1508     * somebody to do a simple decode (without filtering, etc.) and
   1509     * then later call us here to do the verification.
   1510     * OR, we can just specify that the interface to this routine
   1511     * *requires* that the digest(s) be done before calling and either
   1512     * stashed in the struct itself or passed in explicitly (as would
   1513     * be done for detached contents).
   1514     */
   1515    if ((digests == NULL || digests[0] == NULL) && (detached_digest == NULL || detached_digest->data == NULL))
   1516        goto done;
   1517 
   1518    /*
   1519     * Find and confirm digest algorithm.
   1520     */
   1521    digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm));
   1522 
   1523    /* make sure we understand the digest type first */
   1524    found_type = HASH_GetHashTypeByOidTag(digestTag);
   1525    if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) {
   1526        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1527        goto done;
   1528    }
   1529 
   1530    if (detached_digest != NULL) {
   1531        unsigned int hashLen = HASH_ResultLen(found_type);
   1532 
   1533        if (digest_type != found_type ||
   1534            detached_digest->len != hashLen) {
   1535            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1536            goto done;
   1537        }
   1538        digest = detached_digest;
   1539    } else {
   1540        PORT_Assert(digestalgs != NULL && digestalgs[0] != NULL);
   1541        if (digestalgs == NULL || digestalgs[0] == NULL) {
   1542            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1543            goto done;
   1544        }
   1545 
   1546        /*
   1547         * pick digest matching signerinfo->digestAlg from digests
   1548         */
   1549        for (i = 0; digestalgs[i] != NULL; i++) {
   1550            if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag)
   1551                break;
   1552        }
   1553        if (digestalgs[i] == NULL) {
   1554            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1555            goto done;
   1556        }
   1557 
   1558        digest = digests[i];
   1559    }
   1560 
   1561    encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm));
   1562    if (encTag == SEC_OID_UNKNOWN) {
   1563        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1564        goto done;
   1565    }
   1566 
   1567    if (signerinfo->authAttr != NULL) {
   1568        SEC_PKCS7Attribute *attr;
   1569        SECItem *value;
   1570        SECItem encoded_attrs;
   1571 
   1572        /*
   1573         * We have a sigkey only for signedAndEnvelopedData, which is
   1574         * not supposed to have any authenticated attributes.
   1575         */
   1576        if (sigkey != NULL) {
   1577            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1578            goto done;
   1579        }
   1580 
   1581        /*
   1582         * PKCS #7 says that if there are any authenticated attributes,
   1583         * then there must be one for content type which matches the
   1584         * content type of the content being signed, and there must
   1585         * be one for message digest which matches our message digest.
   1586         * So check these things first.
   1587         * XXX Might be nice to have a compare-attribute-value function
   1588         * which could collapse the following nicely.
   1589         */
   1590        attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
   1591                                      SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
   1592        value = sec_PKCS7AttributeValue(attr);
   1593        if (value == NULL || value->len != content_type->len) {
   1594            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1595            goto done;
   1596        }
   1597        if (PORT_Memcmp(value->data, content_type->data, value->len) != 0) {
   1598            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1599            goto done;
   1600        }
   1601 
   1602        attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
   1603                                      SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
   1604        value = sec_PKCS7AttributeValue(attr);
   1605        if (value == NULL || value->len != digest->len) {
   1606            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1607            goto done;
   1608        }
   1609        if (PORT_Memcmp(value->data, digest->data, value->len) != 0) {
   1610            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1611            goto done;
   1612        }
   1613 
   1614        /*
   1615         * Okay, we met the constraints of the basic attributes.
   1616         * Now check the signature, which is based on a digest of
   1617         * the DER-encoded authenticated attributes.  So, first we
   1618         * encode and then we digest/verify.
   1619         */
   1620        encoded_attrs.data = NULL;
   1621        encoded_attrs.len = 0;
   1622        if (sec_PKCS7EncodeAttributes(NULL, &encoded_attrs,
   1623                                      &(signerinfo->authAttr)) == NULL)
   1624            goto done;
   1625 
   1626        if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
   1627            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1628            goto done;
   1629        }
   1630 
   1631        goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data,
   1632                                                encoded_attrs.len,
   1633                                                publickey, &(signerinfo->encDigest),
   1634                                                encTag, digestTag, NULL,
   1635                                                cinfo->pwfn_arg) == SECSuccess);
   1636        PORT_Free(encoded_attrs.data);
   1637    } else {
   1638        SECItem *sig;
   1639        SECItem holder;
   1640 
   1641        /*
   1642         * No authenticated attributes.
   1643         * The signature is based on the plain message digest.
   1644         */
   1645 
   1646        sig = &(signerinfo->encDigest);
   1647        if (sig->len == 0) { /* bad signature */
   1648            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1649            goto done;
   1650        }
   1651 
   1652        if (sigkey != NULL) {
   1653            sec_PKCS7CipherObject *decryptobj;
   1654            unsigned int buflen;
   1655 
   1656            /*
   1657             * For signedAndEnvelopedData, we first must decrypt the encrypted
   1658             * digest with the bulk encryption key.  The result is the normal
   1659             * encrypted digest (aka the signature).
   1660             */
   1661            decryptobj = sec_PKCS7CreateDecryptObject(sigkey, bulkid);
   1662            if (decryptobj == NULL)
   1663                goto done;
   1664 
   1665            buflen = sec_PKCS7DecryptLength(decryptobj, sig->len, PR_TRUE);
   1666            PORT_Assert(buflen);
   1667            if (buflen == 0) { /* something is wrong */
   1668                sec_PKCS7DestroyDecryptObject(decryptobj);
   1669                goto done;
   1670            }
   1671 
   1672            holder.data = (unsigned char *)PORT_Alloc(buflen);
   1673            if (holder.data == NULL) {
   1674                sec_PKCS7DestroyDecryptObject(decryptobj);
   1675                goto done;
   1676            }
   1677 
   1678            rv = sec_PKCS7Decrypt(decryptobj, holder.data, &holder.len, buflen,
   1679                                  sig->data, sig->len, PR_TRUE);
   1680            sec_PKCS7DestroyDecryptObject(decryptobj);
   1681            if (rv != SECSuccess) {
   1682                goto done;
   1683            }
   1684 
   1685            sig = &holder;
   1686        }
   1687 
   1688        goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig,
   1689                                                  encTag, digestTag, cinfo->pwfn_arg) == SECSuccess);
   1690 
   1691        if (sigkey != NULL) {
   1692            PORT_Assert(sig == &holder);
   1693            PORT_ZFree(holder.data, holder.len);
   1694        }
   1695    }
   1696 
   1697    if (!goodsig) {
   1698        /*
   1699         * XXX Change the generic error into our specific one, because
   1700         * in that case we get a better explanation out of the Security
   1701         * Advisor.  This is really a bug in our error strings (the
   1702         * "generic" error has a lousy/wrong message associated with it
   1703         * which assumes the signature verification was done for the
   1704         * purposes of checking the issuer signature on a certificate)
   1705         * but this is at least an easy workaround and/or in the
   1706         * Security Advisor, which specifically checks for the error
   1707         * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
   1708         * in that case but does not similarly check for
   1709         * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
   1710         * probably say the wrong thing in the case that it *was* the
   1711         * certificate signature check that failed during the cert
   1712         * verification done above.  Our error handling is really a mess.
   1713         */
   1714        if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
   1715            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   1716    }
   1717 
   1718 savecert:
   1719    /*
   1720     * Only save the smime profile if we are checking an email message and
   1721     * the cert has an email address in it.
   1722     */
   1723    if (cert->emailAddr && cert->emailAddr[0] &&
   1724        ((certusage == certUsageEmailSigner) ||
   1725         (certusage == certUsageEmailRecipient))) {
   1726        SECItem *profile = NULL;
   1727        int save_error;
   1728 
   1729        /*
   1730         * Remember the current error set because we do not care about
   1731         * anything set by the functions we are about to call.
   1732         */
   1733        save_error = PORT_GetError();
   1734 
   1735        if (goodsig && (signerinfo->authAttr != NULL)) {
   1736            /*
   1737             * If the signature is good, then we can save the S/MIME profile,
   1738             * if we have one.
   1739             */
   1740            SEC_PKCS7Attribute *attr;
   1741 
   1742            attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
   1743                                          SEC_OID_PKCS9_SMIME_CAPABILITIES,
   1744                                          PR_TRUE);
   1745            profile = sec_PKCS7AttributeValue(attr);
   1746        }
   1747 
   1748        rv = CERT_SaveSMimeProfile(cert, profile, encoded_stime);
   1749 
   1750        /*
   1751         * Restore the saved error in case the calls above set a new
   1752         * one that we do not actually care about.
   1753         */
   1754        PORT_SetError(save_error);
   1755 
   1756        /*
   1757         * XXX Failure is not indicated anywhere -- the signature
   1758         * verification itself is unaffected by whether or not the
   1759         * profile was successfully saved.
   1760         */
   1761    }
   1762 
   1763 done:
   1764 
   1765    /*
   1766     * See comment above about why we do not want to destroy cert
   1767     * itself here.
   1768     */
   1769 
   1770    if (certs != NULL)
   1771        CERT_DestroyCertArray(certs, certcount);
   1772 
   1773    if (publickey != NULL)
   1774        SECKEY_DestroyPublicKey(publickey);
   1775 
   1776    return goodsig;
   1777 }
   1778 
   1779 /*
   1780 * SEC_PKCS7VerifySignature
   1781 *      Look at a PKCS7 contentInfo and check if the signature is good.
   1782 *      The verification checks that the signing cert is valid and trusted
   1783 *      for the purpose specified by "certusage".
   1784 *
   1785 *      In addition, if "keepcerts" is true, add any new certificates found
   1786 *      into our local database.
   1787 */
   1788 PRBool
   1789 SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
   1790                         SECCertUsage certusage,
   1791                         PRBool keepcerts)
   1792 {
   1793    return sec_pkcs7_verify_signature(cinfo, certusage,
   1794                                      NULL, HASH_AlgNULL, keepcerts, NULL);
   1795 }
   1796 
   1797 /*
   1798 * SEC_PKCS7VerifyDetachedSignature
   1799 *      Look at a PKCS7 contentInfo and check if the signature matches
   1800 *      a passed-in digest (calculated, supposedly, from detached contents).
   1801 *      The verification checks that the signing cert is valid and trusted
   1802 *      for the purpose specified by "certusage".
   1803 *
   1804 *      In addition, if "keepcerts" is true, add any new certificates found
   1805 *      into our local database.
   1806 */
   1807 PRBool
   1808 SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
   1809                                 SECCertUsage certusage,
   1810                                 const SECItem *detached_digest,
   1811                                 HASH_HashType digest_type,
   1812                                 PRBool keepcerts)
   1813 {
   1814    return sec_pkcs7_verify_signature(cinfo, certusage,
   1815                                      detached_digest, digest_type,
   1816                                      keepcerts, NULL);
   1817 }
   1818 
   1819 /*
   1820 * SEC_PKCS7VerifyDetachedSignatureAtTime
   1821 *      Look at a PKCS7 contentInfo and check if the signature matches
   1822 *      a passed-in digest (calculated, supposedly, from detached contents).
   1823 *      The verification checks that the signing cert is valid and trusted
   1824 *      for the purpose specified by "certusage" at time "atTime".
   1825 *
   1826 *      In addition, if "keepcerts" is true, add any new certificates found
   1827 *      into our local database.
   1828 */
   1829 PRBool
   1830 SEC_PKCS7VerifyDetachedSignatureAtTime(SEC_PKCS7ContentInfo *cinfo,
   1831                                       SECCertUsage certusage,
   1832                                       const SECItem *detached_digest,
   1833                                       HASH_HashType digest_type,
   1834                                       PRBool keepcerts,
   1835                                       PRTime atTime)
   1836 {
   1837    return sec_pkcs7_verify_signature(cinfo, certusage,
   1838                                      detached_digest, digest_type,
   1839                                      keepcerts, &atTime);
   1840 }
   1841 
   1842 /*
   1843 * Return the asked-for portion of the name of the signer of a PKCS7
   1844 * signed object.
   1845 *
   1846 * Returns a pointer to allocated memory, which must be freed.
   1847 * A NULL return value is an error.
   1848 */
   1849 
   1850 #define sec_common_name 1
   1851 #define sec_email_address 2
   1852 
   1853 static char *
   1854 sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
   1855 {
   1856    SECOidTag kind;
   1857    SEC_PKCS7SignerInfo **signerinfos;
   1858    CERTCertificate *signercert;
   1859    char *container;
   1860 
   1861    kind = SEC_PKCS7ContentType(cinfo);
   1862    switch (kind) {
   1863        default:
   1864        case SEC_OID_PKCS7_DATA:
   1865        case SEC_OID_PKCS7_DIGESTED_DATA:
   1866        case SEC_OID_PKCS7_ENVELOPED_DATA:
   1867        case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1868            PORT_Assert(0);
   1869            return NULL;
   1870        case SEC_OID_PKCS7_SIGNED_DATA: {
   1871            SEC_PKCS7SignedData *sdp;
   1872 
   1873            sdp = cinfo->content.signedData;
   1874            signerinfos = sdp->signerInfos;
   1875        } break;
   1876        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
   1877            SEC_PKCS7SignedAndEnvelopedData *saedp;
   1878 
   1879            saedp = cinfo->content.signedAndEnvelopedData;
   1880            signerinfos = saedp->signerInfos;
   1881        } break;
   1882    }
   1883 
   1884    if (signerinfos == NULL || signerinfos[0] == NULL)
   1885        return NULL;
   1886 
   1887    signercert = signerinfos[0]->cert;
   1888 
   1889    /*
   1890     * No cert there; see if we can find one by calling verify ourselves.
   1891     */
   1892    if (signercert == NULL) {
   1893        /*
   1894         * The cert usage does not matter in this case, because we do not
   1895         * actually care about the verification itself, but we have to pick
   1896         * some valid usage to pass in.
   1897         */
   1898        (void)sec_pkcs7_verify_signature(cinfo, certUsageEmailSigner,
   1899                                         NULL, HASH_AlgNULL, PR_FALSE, NULL);
   1900        signercert = signerinfos[0]->cert;
   1901        if (signercert == NULL)
   1902            return NULL;
   1903    }
   1904 
   1905    switch (selector) {
   1906        case sec_common_name:
   1907            container = CERT_GetCommonName(&signercert->subject);
   1908            break;
   1909        case sec_email_address:
   1910            if (signercert->emailAddr && signercert->emailAddr[0]) {
   1911                container = PORT_Strdup(signercert->emailAddr);
   1912            } else {
   1913                container = NULL;
   1914            }
   1915            break;
   1916        default:
   1917            PORT_Assert(0);
   1918            container = NULL;
   1919            break;
   1920    }
   1921 
   1922    return container;
   1923 }
   1924 
   1925 char *
   1926 SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
   1927 {
   1928    return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
   1929 }
   1930 
   1931 char *
   1932 SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
   1933 {
   1934    return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
   1935 }
   1936 
   1937 /*
   1938 * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
   1939 */
   1940 SECItem *
   1941 SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
   1942 {
   1943    SEC_PKCS7SignerInfo **signerinfos;
   1944    SEC_PKCS7Attribute *attr;
   1945 
   1946    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
   1947        return NULL;
   1948 
   1949    signerinfos = cinfo->content.signedData->signerInfos;
   1950 
   1951    /*
   1952     * No signature, or more than one, means no deal.
   1953     */
   1954    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
   1955        return NULL;
   1956 
   1957    attr = sec_PKCS7FindAttribute(signerinfos[0]->authAttr,
   1958                                  SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
   1959    return sec_PKCS7AttributeValue(attr);
   1960 }