tor-browser

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

p7encode.c (34940B)


      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 encoding.
      7 */
      8 
      9 #include "p7local.h"
     10 
     11 #include "cert.h"
     12 #include "cryptohi.h"
     13 #include "keyhi.h"
     14 #include "secasn1.h"
     15 #include "secoid.h"
     16 #include "secitem.h"
     17 #include "pk11func.h"
     18 #include "secerr.h"
     19 #include "sechash.h" /* for HASH_GetHashObject() */
     20 
     21 struct sec_pkcs7_encoder_output {
     22    SEC_PKCS7EncoderOutputCallback outputfn;
     23    void *outputarg;
     24 };
     25 
     26 struct SEC_PKCS7EncoderContextStr {
     27    SEC_ASN1EncoderContext *ecx;
     28    SEC_PKCS7ContentInfo *cinfo;
     29    struct sec_pkcs7_encoder_output output;
     30    sec_PKCS7CipherObject *encryptobj;
     31    const SECHashObject *digestobj;
     32    void *digestcx;
     33 };
     34 
     35 /*
     36 * The little output function that the ASN.1 encoder calls to hand
     37 * us bytes which we in turn hand back to our caller (via the callback
     38 * they gave us).
     39 */
     40 static void
     41 sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len,
     42                      int depth, SEC_ASN1EncodingPart data_kind)
     43 {
     44    struct sec_pkcs7_encoder_output *output;
     45 
     46    output = (struct sec_pkcs7_encoder_output *)arg;
     47    output->outputfn(output->outputarg, buf, len);
     48 }
     49 
     50 static sec_PKCS7CipherObject *
     51 sec_pkcs7_encoder_start_encrypt(SEC_PKCS7ContentInfo *cinfo,
     52                                PK11SymKey *orig_bulkkey)
     53 {
     54    SECOidTag kind;
     55    sec_PKCS7CipherObject *encryptobj;
     56    SEC_PKCS7RecipientInfo **recipientinfos, *ri;
     57    SEC_PKCS7EncryptedContentInfo *enccinfo;
     58    SECKEYPublicKey *publickey = NULL;
     59    SECKEYPrivateKey *ourPrivKey = NULL;
     60    PK11SymKey *bulkkey;
     61    void *mark;
     62    int i;
     63    PLArenaPool *arena = NULL;
     64 
     65    kind = SEC_PKCS7ContentType(cinfo);
     66    switch (kind) {
     67        default:
     68        case SEC_OID_PKCS7_DATA:
     69        case SEC_OID_PKCS7_DIGESTED_DATA:
     70        case SEC_OID_PKCS7_SIGNED_DATA:
     71            recipientinfos = NULL;
     72            enccinfo = NULL;
     73            break;
     74        case SEC_OID_PKCS7_ENCRYPTED_DATA: {
     75            SEC_PKCS7EncryptedData *encdp;
     76 
     77            /* To do EncryptedData we *must* be given a bulk key. */
     78            PORT_Assert(orig_bulkkey != NULL);
     79            if (orig_bulkkey == NULL) {
     80                /* XXX error? */
     81                return NULL;
     82            }
     83 
     84            encdp = cinfo->content.encryptedData;
     85            recipientinfos = NULL;
     86            enccinfo = &(encdp->encContentInfo);
     87        } break;
     88        case SEC_OID_PKCS7_ENVELOPED_DATA: {
     89            SEC_PKCS7EnvelopedData *envdp;
     90 
     91            envdp = cinfo->content.envelopedData;
     92            recipientinfos = envdp->recipientInfos;
     93            enccinfo = &(envdp->encContentInfo);
     94        } break;
     95        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
     96            SEC_PKCS7SignedAndEnvelopedData *saedp;
     97 
     98            saedp = cinfo->content.signedAndEnvelopedData;
     99            recipientinfos = saedp->recipientInfos;
    100            enccinfo = &(saedp->encContentInfo);
    101        } break;
    102    }
    103 
    104    if (enccinfo == NULL)
    105        return NULL;
    106 
    107    bulkkey = orig_bulkkey;
    108    if (bulkkey == NULL) {
    109        CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg);
    110        PK11SlotInfo *slot;
    111 
    112        slot = PK11_GetBestSlot(type, cinfo->pwfn_arg);
    113        if (slot == NULL) {
    114            return NULL;
    115        }
    116        bulkkey = PK11_KeyGen(slot, type, NULL, enccinfo->keysize / 8,
    117                              cinfo->pwfn_arg);
    118        PK11_FreeSlot(slot);
    119        if (bulkkey == NULL) {
    120            return NULL;
    121        }
    122    }
    123 
    124    encryptobj = NULL;
    125    mark = PORT_ArenaMark(cinfo->poolp);
    126 
    127    /*
    128     * Encrypt the bulk key with the public key of each recipient.
    129     */
    130    for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) {
    131        CERTCertificate *cert;
    132        SECOidTag certalgtag, encalgtag;
    133        SECStatus rv;
    134        int data_len;
    135        SECItem *params = NULL;
    136 
    137        cert = ri->cert;
    138        PORT_Assert(cert != NULL);
    139        if (cert == NULL)
    140            continue;
    141 
    142        /*
    143         * XXX Want an interface that takes a cert and some data and
    144         * fills in an algorithmID and encrypts the data with the public
    145         * key from the cert.  Or, give me two interfaces -- one which
    146         * gets the algorithm tag from a cert (I should not have to go
    147         * down into the subjectPublicKeyInfo myself) and another which
    148         * takes a public key and algorithm tag and data and encrypts
    149         * the data.  Or something like that.  The point is that all
    150         * of the following hardwired RSA stuff should be done elsewhere.
    151         */
    152 
    153        certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
    154 
    155        switch (certalgtag) {
    156            case SEC_OID_PKCS1_RSA_ENCRYPTION:
    157                encalgtag = certalgtag;
    158                publickey = CERT_ExtractPublicKey(cert);
    159                if (publickey == NULL)
    160                    goto loser;
    161 
    162                data_len = SECKEY_PublicKeyStrength(publickey);
    163                ri->encKey.data =
    164                    (unsigned char *)PORT_ArenaAlloc(cinfo->poolp, data_len);
    165                ri->encKey.len = data_len;
    166                if (ri->encKey.data == NULL)
    167                    goto loser;
    168 
    169                rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag), publickey,
    170                                        bulkkey, &ri->encKey);
    171 
    172                SECKEY_DestroyPublicKey(publickey);
    173                publickey = NULL;
    174                if (rv != SECSuccess)
    175                    goto loser;
    176                params = NULL; /* paranoia */
    177                break;
    178            default:
    179                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    180                goto loser;
    181        }
    182 
    183        rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag,
    184                                   params);
    185        if (rv != SECSuccess)
    186            goto loser;
    187        if (arena)
    188            PORT_FreeArena(arena, PR_FALSE);
    189        arena = NULL;
    190    }
    191 
    192    encryptobj = sec_PKCS7CreateEncryptObject(cinfo->poolp, bulkkey,
    193                                              enccinfo->encalg,
    194                                              &(enccinfo->contentEncAlg));
    195    if (encryptobj != NULL) {
    196        PORT_ArenaUnmark(cinfo->poolp, mark);
    197        mark = NULL; /* good one; do not want to release */
    198    }
    199    /* fallthru */
    200 
    201 loser:
    202    if (arena) {
    203        PORT_FreeArena(arena, PR_FALSE);
    204    }
    205    if (publickey) {
    206        SECKEY_DestroyPublicKey(publickey);
    207    }
    208    if (ourPrivKey) {
    209        SECKEY_DestroyPrivateKey(ourPrivKey);
    210    }
    211    if (mark != NULL) {
    212        PORT_ArenaRelease(cinfo->poolp, mark);
    213    }
    214    if (orig_bulkkey == NULL) {
    215        if (bulkkey)
    216            PK11_FreeSymKey(bulkkey);
    217    }
    218 
    219    return encryptobj;
    220 }
    221 
    222 static void
    223 sec_pkcs7_encoder_notify(void *arg, PRBool before, void *dest, int depth)
    224 {
    225    SEC_PKCS7EncoderContext *p7ecx;
    226    SEC_PKCS7ContentInfo *cinfo;
    227    SECOidTag kind;
    228    PRBool before_content;
    229 
    230    /*
    231     * We want to notice just before the content field.  After fields are
    232     * not interesting to us.
    233     */
    234    if (!before)
    235        return;
    236 
    237    p7ecx = (SEC_PKCS7EncoderContext *)arg;
    238    cinfo = p7ecx->cinfo;
    239 
    240    before_content = PR_FALSE;
    241 
    242    /*
    243     * Watch for the content field, at which point we want to instruct
    244     * the ASN.1 encoder to start taking bytes from the buffer.
    245     *
    246     * XXX The following assumes the inner content type is data;
    247     * if/when we want to handle fully nested types, this will have
    248     * to recurse until reaching the innermost data content.
    249     */
    250    kind = SEC_PKCS7ContentType(cinfo);
    251    switch (kind) {
    252        default:
    253        case SEC_OID_PKCS7_DATA:
    254            if (dest == &(cinfo->content.data))
    255                before_content = PR_TRUE;
    256            break;
    257 
    258        case SEC_OID_PKCS7_DIGESTED_DATA: {
    259            SEC_PKCS7DigestedData *digd;
    260 
    261            digd = cinfo->content.digestedData;
    262            if (digd == NULL)
    263                break;
    264 
    265            if (dest == &(digd->contentInfo.content))
    266                before_content = PR_TRUE;
    267        } break;
    268 
    269        case SEC_OID_PKCS7_ENCRYPTED_DATA: {
    270            SEC_PKCS7EncryptedData *encd;
    271 
    272            encd = cinfo->content.encryptedData;
    273            if (encd == NULL)
    274                break;
    275 
    276            if (dest == &(encd->encContentInfo.encContent))
    277                before_content = PR_TRUE;
    278        } break;
    279 
    280        case SEC_OID_PKCS7_ENVELOPED_DATA: {
    281            SEC_PKCS7EnvelopedData *envd;
    282 
    283            envd = cinfo->content.envelopedData;
    284            if (envd == NULL)
    285                break;
    286 
    287            if (dest == &(envd->encContentInfo.encContent))
    288                before_content = PR_TRUE;
    289        } break;
    290 
    291        case SEC_OID_PKCS7_SIGNED_DATA: {
    292            SEC_PKCS7SignedData *sigd;
    293 
    294            sigd = cinfo->content.signedData;
    295            if (sigd == NULL)
    296                break;
    297 
    298            if (dest == &(sigd->contentInfo.content))
    299                before_content = PR_TRUE;
    300        } break;
    301 
    302        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    303            SEC_PKCS7SignedAndEnvelopedData *saed;
    304 
    305            saed = cinfo->content.signedAndEnvelopedData;
    306            if (saed == NULL)
    307                break;
    308 
    309            if (dest == &(saed->encContentInfo.encContent))
    310                before_content = PR_TRUE;
    311        } break;
    312    }
    313 
    314    if (before_content) {
    315        /*
    316         * This will cause the next SEC_ASN1EncoderUpdate to take the
    317         * contents bytes from the passed-in buffer.
    318         */
    319        SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
    320        /*
    321         * And that is all we needed this notify function for.
    322         */
    323        SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);
    324    }
    325 }
    326 
    327 static SEC_PKCS7EncoderContext *
    328 sec_pkcs7_encoder_start_contexts(SEC_PKCS7ContentInfo *cinfo,
    329                                 PK11SymKey *bulkkey)
    330 {
    331    SEC_PKCS7EncoderContext *p7ecx;
    332    SECOidTag kind;
    333    PRBool encrypt;
    334    SECItem **digests;
    335    SECAlgorithmID *digestalg, **digestalgs;
    336 
    337    p7ecx =
    338        (SEC_PKCS7EncoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7EncoderContext));
    339    if (p7ecx == NULL)
    340        return NULL;
    341 
    342    digests = NULL;
    343    digestalg = NULL;
    344    digestalgs = NULL;
    345    encrypt = PR_FALSE;
    346 
    347    kind = SEC_PKCS7ContentType(cinfo);
    348    switch (kind) {
    349        default:
    350        case SEC_OID_PKCS7_DATA:
    351            break;
    352        case SEC_OID_PKCS7_DIGESTED_DATA:
    353            digestalg = &(cinfo->content.digestedData->digestAlg);
    354            break;
    355        case SEC_OID_PKCS7_SIGNED_DATA:
    356            digests = cinfo->content.signedData->digests;
    357            digestalgs = cinfo->content.signedData->digestAlgorithms;
    358            break;
    359        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    360        case SEC_OID_PKCS7_ENVELOPED_DATA:
    361            encrypt = PR_TRUE;
    362            break;
    363        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    364            digests = cinfo->content.signedAndEnvelopedData->digests;
    365            digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms;
    366            encrypt = PR_TRUE;
    367            break;
    368    }
    369 
    370    if (encrypt) {
    371        p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt(cinfo, bulkkey);
    372        if (p7ecx->encryptobj == NULL) {
    373            PORT_Free(p7ecx);
    374            return NULL;
    375        }
    376    }
    377 
    378    if (digestalgs != NULL) {
    379        if (digests != NULL) {
    380            /* digests already created (probably for detached data) */
    381            digestalg = NULL;
    382        } else {
    383            /*
    384             * XXX Some day we should handle multiple digests; for now,
    385             * assume only one will be done.
    386             */
    387            PORT_Assert(digestalgs[0] != NULL && digestalgs[1] == NULL);
    388            digestalg = digestalgs[0];
    389        }
    390    }
    391 
    392    if (digestalg != NULL) {
    393        SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm));
    394 
    395        p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag);
    396        if (p7ecx->digestobj != NULL) {
    397            p7ecx->digestcx = (*p7ecx->digestobj->create)();
    398            if (p7ecx->digestcx == NULL)
    399                p7ecx->digestobj = NULL;
    400            else
    401                (*p7ecx->digestobj->begin)(p7ecx->digestcx);
    402        }
    403        if (p7ecx->digestobj == NULL) {
    404            if (p7ecx->encryptobj != NULL)
    405                sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
    406            PORT_Free(p7ecx);
    407            return NULL;
    408        }
    409    }
    410 
    411    p7ecx->cinfo = cinfo;
    412    return p7ecx;
    413 }
    414 
    415 SEC_PKCS7EncoderContext *
    416 SEC_PKCS7EncoderStart(SEC_PKCS7ContentInfo *cinfo,
    417                      SEC_PKCS7EncoderOutputCallback outputfn,
    418                      void *outputarg,
    419                      PK11SymKey *bulkkey)
    420 {
    421    SEC_PKCS7EncoderContext *p7ecx;
    422    SECStatus rv;
    423 
    424    p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
    425    if (p7ecx == NULL)
    426        return NULL;
    427 
    428    p7ecx->output.outputfn = outputfn;
    429    p7ecx->output.outputarg = outputarg;
    430 
    431    /*
    432     * Initialize the BER encoder.
    433     */
    434    p7ecx->ecx = SEC_ASN1EncoderStart(cinfo, sec_PKCS7ContentInfoTemplate,
    435                                      sec_pkcs7_encoder_out, &(p7ecx->output));
    436    if (p7ecx->ecx == NULL) {
    437        PORT_Free(p7ecx);
    438        return NULL;
    439    }
    440 
    441    /*
    442     * Indicate that we are streaming.  We will be streaming until we
    443     * get past the contents bytes.
    444     */
    445    SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
    446 
    447    /*
    448     * The notify function will watch for the contents field.
    449     */
    450    SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx);
    451 
    452    /*
    453     * This will encode everything up to the content bytes.  (The notify
    454     * function will then cause the encoding to stop there.)  Then our
    455     * caller can start passing contents bytes to our Update, which we
    456     * will pass along.
    457     */
    458    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
    459    if (rv != SECSuccess) {
    460        PORT_Free(p7ecx);
    461        return NULL;
    462    }
    463 
    464    return p7ecx;
    465 }
    466 
    467 /*
    468 * XXX If/when we support nested contents, this needs to be revised.
    469 */
    470 static SECStatus
    471 sec_pkcs7_encoder_work_data(SEC_PKCS7EncoderContext *p7ecx, SECItem *dest,
    472                            const unsigned char *data, unsigned long len,
    473                            PRBool final)
    474 {
    475    unsigned char *buf = NULL;
    476    SECStatus rv;
    477 
    478    rv = SECSuccess; /* may as well be optimistic */
    479 
    480    /*
    481     * We should really have data to process, or we should be trying
    482     * to finish/flush the last block.  (This is an overly paranoid
    483     * check since all callers are in this file and simple inspection
    484     * proves they do it right.  But it could find a bug in future
    485     * modifications/development, that is why it is here.)
    486     */
    487    PORT_Assert((data != NULL && len) || final);
    488 
    489    /*
    490     * Update the running digest.
    491     * XXX This needs modification if/when we handle multiple digests.
    492     */
    493    if (len && p7ecx->digestobj != NULL) {
    494        (*p7ecx->digestobj->update)(p7ecx->digestcx, data, len);
    495    }
    496 
    497    /*
    498     * Encrypt this chunk.
    499     */
    500    if (p7ecx->encryptobj != NULL) {
    501        /* XXX the following lengths should all be longs? */
    502        unsigned int inlen;  /* length of data being encrypted */
    503        unsigned int outlen; /* length of encrypted data */
    504        unsigned int buflen; /* length available for encrypted data */
    505 
    506        inlen = len;
    507        buflen = sec_PKCS7EncryptLength(p7ecx->encryptobj, inlen, final);
    508        if (buflen == 0) {
    509            /*
    510             * No output is expected, but the input data may be buffered
    511             * so we still have to call Encrypt.
    512             */
    513            rv = sec_PKCS7Encrypt(p7ecx->encryptobj, NULL, &outlen, 0,
    514                                  data, inlen, final);
    515            if (final) {
    516                len = 0;
    517                goto done;
    518            }
    519            return rv;
    520        }
    521 
    522        if (dest != NULL)
    523            buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen);
    524        else
    525            buf = (unsigned char *)PORT_Alloc(buflen);
    526 
    527        if (buf == NULL) {
    528            rv = SECFailure;
    529        } else {
    530            rv = sec_PKCS7Encrypt(p7ecx->encryptobj, buf, &outlen, buflen,
    531                                  data, inlen, final);
    532            data = buf;
    533            len = outlen;
    534        }
    535        if (rv != SECSuccess) {
    536            if (final)
    537                goto done;
    538            return rv;
    539        }
    540    }
    541 
    542    if (p7ecx->ecx != NULL) {
    543        /*
    544         * Encode the contents bytes.
    545         */
    546        if (len) {
    547            rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
    548        }
    549    }
    550 
    551 done:
    552    if (p7ecx->encryptobj != NULL) {
    553        if (final)
    554            sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj);
    555        if (dest != NULL) {
    556            dest->data = buf;
    557            dest->len = len;
    558        } else if (buf != NULL) {
    559            PORT_Free(buf);
    560        }
    561    }
    562 
    563    if (final && p7ecx->digestobj != NULL) {
    564        SECItem *digest, **digests, ***digestsp;
    565        unsigned char *digdata;
    566        SECOidTag kind;
    567 
    568        kind = SEC_PKCS7ContentType(p7ecx->cinfo);
    569        switch (kind) {
    570            default:
    571                PORT_Assert(0);
    572                return SECFailure;
    573            case SEC_OID_PKCS7_DIGESTED_DATA:
    574                digest = &(p7ecx->cinfo->content.digestedData->digest);
    575                digestsp = NULL;
    576                break;
    577            case SEC_OID_PKCS7_SIGNED_DATA:
    578                digest = NULL;
    579                digestsp = &(p7ecx->cinfo->content.signedData->digests);
    580                break;
    581            case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    582                digest = NULL;
    583                digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests);
    584                break;
    585        }
    586 
    587        digdata = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
    588                                                   p7ecx->digestobj->length);
    589        if (digdata == NULL)
    590            return SECFailure;
    591 
    592        if (digestsp != NULL) {
    593            PORT_Assert(digest == NULL);
    594 
    595            digest = (SECItem *)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
    596                                                sizeof(SECItem));
    597            digests = (SECItem **)PORT_ArenaAlloc(p7ecx->cinfo->poolp,
    598                                                  2 * sizeof(SECItem *));
    599            if (digests == NULL || digest == NULL)
    600                return SECFailure;
    601 
    602            digests[0] = digest;
    603            digests[1] = NULL;
    604 
    605            *digestsp = digests;
    606        }
    607 
    608        PORT_Assert(digest != NULL);
    609 
    610        digest->data = digdata;
    611        digest->len = p7ecx->digestobj->length;
    612 
    613        (*p7ecx->digestobj->end)(p7ecx->digestcx, digest->data,
    614                                 &(digest->len), digest->len);
    615        (*p7ecx->digestobj->destroy)(p7ecx->digestcx, PR_TRUE);
    616    }
    617 
    618    return rv;
    619 }
    620 
    621 SECStatus
    622 SEC_PKCS7EncoderUpdate(SEC_PKCS7EncoderContext *p7ecx,
    623                       const char *data, unsigned long len)
    624 {
    625    /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
    626    return sec_pkcs7_encoder_work_data(p7ecx, NULL,
    627                                       (const unsigned char *)data, len,
    628                                       PR_FALSE);
    629 }
    630 
    631 static SECStatus
    632 sec_pkcs7_encoder_sig_and_certs(SEC_PKCS7ContentInfo *cinfo,
    633                                SECKEYGetPasswordKey pwfn, void *pwfnarg)
    634 {
    635    SECOidTag kind;
    636    CERTCertificate **certs;
    637    CERTCertificateList **certlists;
    638    SECAlgorithmID **digestalgs;
    639    SECItem **digests;
    640    SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
    641    SECItem **rawcerts, ***rawcertsp;
    642    PLArenaPool *poolp;
    643    int certcount;
    644    int ci, cli, rci, si;
    645 
    646    kind = SEC_PKCS7ContentType(cinfo);
    647    switch (kind) {
    648        default:
    649        case SEC_OID_PKCS7_DATA:
    650        case SEC_OID_PKCS7_DIGESTED_DATA:
    651        case SEC_OID_PKCS7_ENCRYPTED_DATA:
    652        case SEC_OID_PKCS7_ENVELOPED_DATA:
    653            certs = NULL;
    654            certlists = NULL;
    655            digestalgs = NULL;
    656            digests = NULL;
    657            signerinfos = NULL;
    658            rawcertsp = NULL;
    659            break;
    660        case SEC_OID_PKCS7_SIGNED_DATA: {
    661            SEC_PKCS7SignedData *sdp;
    662 
    663            sdp = cinfo->content.signedData;
    664            certs = sdp->certs;
    665            certlists = sdp->certLists;
    666            digestalgs = sdp->digestAlgorithms;
    667            digests = sdp->digests;
    668            signerinfos = sdp->signerInfos;
    669            rawcertsp = &(sdp->rawCerts);
    670        } break;
    671        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
    672            SEC_PKCS7SignedAndEnvelopedData *saedp;
    673 
    674            saedp = cinfo->content.signedAndEnvelopedData;
    675            certs = saedp->certs;
    676            certlists = saedp->certLists;
    677            digestalgs = saedp->digestAlgorithms;
    678            digests = saedp->digests;
    679            signerinfos = saedp->signerInfos;
    680            rawcertsp = &(saedp->rawCerts);
    681        } break;
    682    }
    683 
    684    if (certs == NULL && certlists == NULL && signerinfos == NULL)
    685        return SECSuccess; /* nothing for us to do! */
    686 
    687    poolp = cinfo->poolp;
    688    certcount = 0;
    689 
    690    if (signerinfos != NULL) {
    691        SECOidTag digestalgtag;
    692        int di;
    693        SECStatus rv;
    694        CERTCertificate *cert;
    695        SECKEYPrivateKey *privkey;
    696        SECItem signature;
    697        SECOidTag signalgtag;
    698 
    699        PORT_Assert(digestalgs != NULL && digests != NULL);
    700 
    701        /*
    702         * If one fails, we bail right then.  If we want to continue and
    703         * try to do subsequent signatures, this loop, and the departures
    704         * from it, will need to be reworked.
    705         */
    706        for (si = 0; signerinfos[si] != NULL; si++) {
    707 
    708            signerinfo = signerinfos[si];
    709 
    710            /* find right digest */
    711            digestalgtag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
    712            for (di = 0; digestalgs[di] != NULL; di++) {
    713                /* XXX Should I be comparing more than the tag? */
    714                if (digestalgtag == SECOID_GetAlgorithmTag(digestalgs[di]))
    715                    break;
    716            }
    717            if (digestalgs[di] == NULL) {
    718                /* XXX oops; do what? set an error? */
    719                return SECFailure;
    720            }
    721            PORT_Assert(digests[di] != NULL);
    722 
    723            cert = signerinfo->cert;
    724            privkey = PK11_FindKeyByAnyCert(cert, pwfnarg);
    725            if (privkey == NULL)
    726                return SECFailure;
    727 
    728            /*
    729             * XXX I think there should be a cert-level interface for this,
    730             * so that I do not have to know about subjectPublicKeyInfo...
    731             */
    732            signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
    733 
    734            if (signerinfo->authAttr != NULL) {
    735                SEC_PKCS7Attribute *attr;
    736                SECItem encoded_attrs;
    737                SECItem *dummy;
    738                SECOidTag algid;
    739 
    740                /*
    741                 * First, find and fill in the message digest attribute.
    742                 */
    743                attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
    744                                              SEC_OID_PKCS9_MESSAGE_DIGEST,
    745                                              PR_TRUE);
    746                PORT_Assert(attr != NULL);
    747                if (attr == NULL) {
    748                    SECKEY_DestroyPrivateKey(privkey);
    749                    return SECFailure;
    750                }
    751 
    752                /*
    753                 * XXX The second half of the following assertion prevents
    754                 * the encoder from being called twice on the same content.
    755                 * Either just remove the second half the assertion, or
    756                 * change the code to check if the value already there is
    757                 * the same as digests[di], whichever seems more right.
    758                 */
    759                PORT_Assert(attr->values != NULL && attr->values[0] == NULL);
    760                attr->values[0] = digests[di];
    761 
    762                /*
    763                 * Before encoding, reorder the attributes so that when they
    764                 * are encoded, they will be conforming DER, which is required
    765                 * to have a specific order and that is what must be used for
    766                 * the hash/signature.  We do this here, rather than building
    767                 * it into EncodeAttributes, because we do not want to do
    768                 * such reordering on incoming messages (which also uses
    769                 * EncodeAttributes) or our old signatures (and other "broken"
    770                 * implementations) will not verify.  So, we want to guarantee
    771                 * that we send out good DER encodings of attributes, but not
    772                 * to expect to receive them.
    773                 */
    774                rv = sec_PKCS7ReorderAttributes(signerinfo->authAttr);
    775                if (rv != SECSuccess) {
    776                    SECKEY_DestroyPrivateKey(privkey);
    777                    return SECFailure;
    778                }
    779 
    780                encoded_attrs.data = NULL;
    781                encoded_attrs.len = 0;
    782                dummy = sec_PKCS7EncodeAttributes(NULL, &encoded_attrs,
    783                                                  &(signerinfo->authAttr));
    784                if (dummy == NULL) {
    785                    SECKEY_DestroyPrivateKey(privkey);
    786                    return SECFailure;
    787                }
    788 
    789                algid = SEC_GetSignatureAlgorithmOidTagByKey(privkey, NULL,
    790                                                             digestalgtag);
    791                if (algid == SEC_OID_UNKNOWN) {
    792                    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    793                    SECKEY_DestroyPrivateKey(privkey);
    794                    return SECFailure;
    795                }
    796                rv = SEC_SignData(&signature,
    797                                  encoded_attrs.data, encoded_attrs.len,
    798                                  privkey,
    799                                  algid);
    800                SECITEM_FreeItem(&encoded_attrs, PR_FALSE);
    801            } else {
    802                rv = SGN_Digest(privkey, digestalgtag, &signature,
    803                                digests[di]);
    804            }
    805 
    806            SECKEY_DestroyPrivateKey(privkey);
    807 
    808            if (rv != SECSuccess)
    809                return rv;
    810 
    811            rv = SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature);
    812            if (rv != SECSuccess)
    813                return rv;
    814 
    815            SECITEM_FreeItem(&signature, PR_FALSE);
    816 
    817            rv = SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
    818                                       signalgtag, NULL);
    819            if (rv != SECSuccess)
    820                return SECFailure;
    821 
    822            /*
    823             * Count the cert chain for this signer.
    824             */
    825            if (signerinfo->certList != NULL)
    826                certcount += signerinfo->certList->len;
    827        }
    828    }
    829 
    830    if (certs != NULL) {
    831        for (ci = 0; certs[ci] != NULL; ci++)
    832            certcount++;
    833    }
    834 
    835    if (certlists != NULL) {
    836        for (cli = 0; certlists[cli] != NULL; cli++)
    837            certcount += certlists[cli]->len;
    838    }
    839 
    840    if (certcount == 0)
    841        return SECSuccess; /* signing done; no certs */
    842 
    843    /*
    844     * Combine all of the certs and cert chains into rawcerts.
    845     * Note: certcount is an upper bound; we may not need that many slots
    846     * but we will allocate anyway to avoid having to do another pass.
    847     * (The temporary space saving is not worth it.)
    848     */
    849    rawcerts = (SECItem **)PORT_ArenaAlloc(poolp,
    850                                           (certcount + 1) * sizeof(SECItem *));
    851    if (rawcerts == NULL)
    852        return SECFailure;
    853 
    854    /*
    855     * XXX Want to check for duplicates and not add *any* cert that is
    856     * already in the set.  This will be more important when we start
    857     * dealing with larger sets of certs, dual-key certs (signing and
    858     * encryption), etc.  For the time being we can slide by...
    859     */
    860    rci = 0;
    861    if (signerinfos != NULL) {
    862        for (si = 0; signerinfos[si] != NULL; si++) {
    863            signerinfo = signerinfos[si];
    864            for (ci = 0; signerinfo->certList && ci < signerinfo->certList->len; ci++)
    865                rawcerts[rci++] = &(signerinfo->certList->certs[ci]);
    866        }
    867    }
    868 
    869    if (certs != NULL) {
    870        for (ci = 0; certs[ci] != NULL; ci++)
    871            rawcerts[rci++] = &(certs[ci]->derCert);
    872    }
    873 
    874    if (certlists != NULL) {
    875        for (cli = 0; certlists[cli] != NULL; cli++) {
    876            for (ci = 0; ci < certlists[cli]->len; ci++)
    877                rawcerts[rci++] = &(certlists[cli]->certs[ci]);
    878        }
    879    }
    880 
    881    rawcerts[rci] = NULL;
    882    *rawcertsp = rawcerts;
    883 
    884    return SECSuccess;
    885 }
    886 
    887 SECStatus
    888 SEC_PKCS7EncoderFinish(SEC_PKCS7EncoderContext *p7ecx,
    889                       SECKEYGetPasswordKey pwfn, void *pwfnarg)
    890 {
    891    SECStatus rv;
    892 
    893    /*
    894     * Flush out any remaining data.
    895     */
    896    rv = sec_pkcs7_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE);
    897 
    898    /*
    899     * Turn off streaming stuff.
    900     */
    901    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
    902    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
    903 
    904    if (rv != SECSuccess)
    905        goto loser;
    906 
    907    rv = sec_pkcs7_encoder_sig_and_certs(p7ecx->cinfo, pwfn, pwfnarg);
    908    if (rv != SECSuccess)
    909        goto loser;
    910 
    911    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
    912 
    913 loser:
    914    SEC_ASN1EncoderFinish(p7ecx->ecx);
    915    PORT_Free(p7ecx);
    916    return rv;
    917 }
    918 
    919 /*
    920 * Abort the ASN.1 stream. Used by pkcs 12
    921 */
    922 void
    923 SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error)
    924 {
    925    PORT_Assert(p7ecx);
    926    SEC_ASN1EncoderAbort(p7ecx->ecx, error);
    927 }
    928 
    929 /*
    930 * After this routine is called, the entire PKCS7 contentInfo is ready
    931 * to be encoded.  This is used internally, but can also be called from
    932 * elsewhere for those who want to be able to just have pointers to
    933 * the ASN1 template for pkcs7 contentInfo built into their own encodings.
    934 */
    935 SECStatus
    936 SEC_PKCS7PrepareForEncode(SEC_PKCS7ContentInfo *cinfo,
    937                          PK11SymKey *bulkkey,
    938                          SECKEYGetPasswordKey pwfn,
    939                          void *pwfnarg)
    940 {
    941    SEC_PKCS7EncoderContext *p7ecx;
    942    SECItem *content, *enc_content;
    943    SECStatus rv;
    944 
    945    p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey);
    946    if (p7ecx == NULL)
    947        return SECFailure;
    948 
    949    content = SEC_PKCS7GetContent(cinfo);
    950 
    951    if (p7ecx->encryptobj != NULL) {
    952        SECOidTag kind;
    953        SEC_PKCS7EncryptedContentInfo *enccinfo;
    954 
    955        kind = SEC_PKCS7ContentType(p7ecx->cinfo);
    956        switch (kind) {
    957            default:
    958                PORT_Assert(0);
    959                rv = SECFailure;
    960                goto loser;
    961            case SEC_OID_PKCS7_ENCRYPTED_DATA:
    962                enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo);
    963                break;
    964            case SEC_OID_PKCS7_ENVELOPED_DATA:
    965                enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo);
    966                break;
    967            case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    968                enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo);
    969                break;
    970        }
    971        enc_content = &(enccinfo->encContent);
    972    } else {
    973        enc_content = NULL;
    974    }
    975 
    976    if (content != NULL && content->data != NULL && content->len) {
    977        rv = sec_pkcs7_encoder_work_data(p7ecx, enc_content,
    978                                         content->data, content->len, PR_TRUE);
    979        if (rv != SECSuccess)
    980            goto loser;
    981    }
    982 
    983    rv = sec_pkcs7_encoder_sig_and_certs(cinfo, pwfn, pwfnarg);
    984 
    985 loser:
    986    PORT_Free(p7ecx);
    987    return rv;
    988 }
    989 
    990 /*
    991 * Encode a PKCS7 object, in one shot.  All necessary components
    992 * of the object must already be specified.  Either the data has
    993 * already been included (via SetContent), or the data is detached,
    994 * or there is no data at all (certs-only).
    995 *
    996 * "cinfo" specifies the object to be encoded.
    997 *
    998 * "outputfn" is where the encoded bytes will be passed.
    999 *
   1000 * "outputarg" is an opaque argument to the above callback.
   1001 *
   1002 * "bulkkey" specifies the bulk encryption key to use.   This argument
   1003 * can be NULL if no encryption is being done, or if the bulk key should
   1004 * be generated internally (usually the case for EnvelopedData but never
   1005 * for EncryptedData, which *must* provide a bulk encryption key).
   1006 *
   1007 * "pwfn" is a callback for getting the password which protects the
   1008 * private key of the signer.  This argument can be NULL if it is known
   1009 * that no signing is going to be done.
   1010 *
   1011 * "pwfnarg" is an opaque argument to the above callback.
   1012 */
   1013 SECStatus
   1014 SEC_PKCS7Encode(SEC_PKCS7ContentInfo *cinfo,
   1015                SEC_PKCS7EncoderOutputCallback outputfn,
   1016                void *outputarg,
   1017                PK11SymKey *bulkkey,
   1018                SECKEYGetPasswordKey pwfn,
   1019                void *pwfnarg)
   1020 {
   1021    SECStatus rv;
   1022 
   1023    rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
   1024    if (rv == SECSuccess) {
   1025        struct sec_pkcs7_encoder_output outputcx;
   1026 
   1027        outputcx.outputfn = outputfn;
   1028        outputcx.outputarg = outputarg;
   1029 
   1030        rv = SEC_ASN1Encode(cinfo, sec_PKCS7ContentInfoTemplate,
   1031                            sec_pkcs7_encoder_out, &outputcx);
   1032    }
   1033 
   1034    return rv;
   1035 }
   1036 
   1037 /*
   1038 * Encode a PKCS7 object, in one shot.  All necessary components
   1039 * of the object must already be specified.  Either the data has
   1040 * already been included (via SetContent), or the data is detached,
   1041 * or there is no data at all (certs-only).  The output, rather than
   1042 * being passed to an output function as is done above, is all put
   1043 * into a SECItem.
   1044 *
   1045 * "pool" specifies a pool from which to allocate the result.
   1046 * It can be NULL, in which case memory is allocated generically.
   1047 *
   1048 * "dest" specifies a SECItem in which to put the result data.
   1049 * It can be NULL, in which case the entire item is allocated, too.
   1050 *
   1051 * "cinfo" specifies the object to be encoded.
   1052 *
   1053 * "bulkkey" specifies the bulk encryption key to use.   This argument
   1054 * can be NULL if no encryption is being done, or if the bulk key should
   1055 * be generated internally (usually the case for EnvelopedData but never
   1056 * for EncryptedData, which *must* provide a bulk encryption key).
   1057 *
   1058 * "pwfn" is a callback for getting the password which protects the
   1059 * private key of the signer.  This argument can be NULL if it is known
   1060 * that no signing is going to be done.
   1061 *
   1062 * "pwfnarg" is an opaque argument to the above callback.
   1063 */
   1064 SECItem *
   1065 SEC_PKCS7EncodeItem(PLArenaPool *pool,
   1066                    SECItem *dest,
   1067                    SEC_PKCS7ContentInfo *cinfo,
   1068                    PK11SymKey *bulkkey,
   1069                    SECKEYGetPasswordKey pwfn,
   1070                    void *pwfnarg)
   1071 {
   1072    SECStatus rv;
   1073 
   1074    rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg);
   1075    if (rv != SECSuccess)
   1076        return NULL;
   1077 
   1078    return SEC_ASN1EncodeItem(pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);
   1079 }