tor-browser

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

cmssigdata.c (33638B)


      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 * CMS signedData methods.
      7 */
      8 
      9 #include "cmslocal.h"
     10 
     11 #include "cert.h"
     12 /*#include "cdbhdl.h"*/
     13 #include "secasn1.h"
     14 #include "secitem.h"
     15 #include "secoid.h"
     16 #include "pk11func.h"
     17 #include "secerr.h"
     18 
     19 NSSCMSSignedData *
     20 NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
     21 {
     22    void *mark;
     23    NSSCMSSignedData *sigd;
     24    PLArenaPool *poolp;
     25 
     26    if (!cmsg) {
     27        PORT_SetError(SEC_ERROR_INVALID_ARGS);
     28        return NULL;
     29    }
     30 
     31    poolp = cmsg->poolp;
     32 
     33    mark = PORT_ArenaMark(poolp);
     34 
     35    sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignedData));
     36    if (sigd == NULL)
     37        goto loser;
     38 
     39    sigd->cmsg = cmsg;
     40 
     41    /* signerInfos, certs, certlists, crls are all empty */
     42    /* version is set in NSS_CMSSignedData_Finalize() */
     43 
     44    PORT_ArenaUnmark(poolp, mark);
     45    return sigd;
     46 
     47 loser:
     48    PORT_ArenaRelease(poolp, mark);
     49    return NULL;
     50 }
     51 
     52 void
     53 NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
     54 {
     55    CERTCertificate **certs, **tempCerts, *cert;
     56    CERTCertificateList **certlists, *certlist;
     57    NSSCMSSignerInfo **signerinfos, *si;
     58 
     59    if (sigd == NULL)
     60        return;
     61 
     62    certs = sigd->certs;
     63    tempCerts = sigd->tempCerts;
     64    certlists = sigd->certLists;
     65    signerinfos = sigd->signerInfos;
     66 
     67    if (certs != NULL) {
     68        while ((cert = *certs++) != NULL)
     69            CERT_DestroyCertificate(cert);
     70    }
     71 
     72    if (tempCerts != NULL) {
     73        while ((cert = *tempCerts++) != NULL)
     74            CERT_DestroyCertificate(cert);
     75    }
     76 
     77    if (certlists != NULL) {
     78        while ((certlist = *certlists++) != NULL)
     79            CERT_DestroyCertificateList(certlist);
     80    }
     81 
     82    if (signerinfos != NULL) {
     83        while ((si = *signerinfos++) != NULL)
     84            NSS_CMSSignerInfo_Destroy(si);
     85    }
     86 
     87    /* everything's in a pool, so don't worry about the storage */
     88    NSS_CMSContentInfo_Destroy(&(sigd->contentInfo));
     89 }
     90 
     91 /*
     92 * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
     93 *     before start of encoding.
     94 *
     95 * In detail:
     96 *  - find out about the right value to put into sigd->version
     97 *  - come up with a list of digestAlgorithms (which should be the union of the algorithms
     98 *         in the signerinfos).
     99 *         If we happen to have a pre-set list of algorithms (and digest values!), we
    100 *         check if we have all the signerinfos' algorithms. If not, this is an error.
    101 */
    102 SECStatus
    103 NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
    104 {
    105    NSSCMSSignerInfo *signerinfo;
    106    SECOidTag digestalgtag;
    107    SECItem *dummy;
    108    int version;
    109    SECStatus rv;
    110    PRBool haveDigests = PR_FALSE;
    111    int n, i;
    112    PLArenaPool *poolp;
    113 
    114    if (!sigd) {
    115        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    116        return SECFailure;
    117    }
    118 
    119    poolp = sigd->cmsg->poolp;
    120 
    121    /* we assume that we have precomputed digests if there is a list of algorithms, and */
    122    /* a chunk of data for each of those algorithms */
    123    if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
    124        for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
    125            if (sigd->digests[i] == NULL)
    126                break;
    127        }
    128        if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
    129            haveDigests = PR_TRUE;             /* yes: we must have all the digests */
    130    }
    131 
    132    version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
    133 
    134    /* RFC2630 5.1 "version is the syntax version number..." */
    135    if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
    136        version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
    137 
    138    /* prepare all the SignerInfos (there may be none) */
    139    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
    140        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
    141 
    142        /* RFC2630 5.1 "version is the syntax version number..." */
    143        if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
    144            version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
    145 
    146        /* collect digestAlgorithms from SignerInfos */
    147        /* (we need to know which algorithms we have when the content comes in) */
    148        /* do not overwrite any existing digestAlgorithms (and digest) */
    149        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    150        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
    151        if (n < 0 && haveDigests) {
    152            /* oops, there is a digestalg we do not have a digest for */
    153            /* but we were supposed to have all the digests already... */
    154            goto loser;
    155        } else if (n < 0) {
    156            /* add the digestAlgorithm & a NULL digest */
    157            rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
    158            if (rv != SECSuccess)
    159                goto loser;
    160        } else {
    161            /* found it, nothing to do */
    162        }
    163    }
    164 
    165    dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
    166    if (dummy == NULL)
    167        return SECFailure;
    168 
    169    /* this is a SET OF, so we need to sort them guys */
    170    rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms,
    171                                SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
    172                                (void **)sigd->digests);
    173    if (rv != SECSuccess)
    174        return SECFailure;
    175 
    176    return SECSuccess;
    177 
    178 loser:
    179    return SECFailure;
    180 }
    181 
    182 SECStatus
    183 NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
    184 {
    185    SECStatus rv;
    186    if (!sigd) {
    187        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    188        return SECFailure;
    189    }
    190    rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
    191    if (rv != SECSuccess) {
    192        return SECFailure;
    193    }
    194    /* set up the digests */
    195    if (sigd->digests && sigd->digests[0]) {
    196        sigd->contentInfo.privateInfo->digcx = NULL; /* don't attempt to make new ones. */
    197    } else if (sigd->digestAlgorithms != NULL) {
    198        sigd->contentInfo.privateInfo->digcx =
    199            NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
    200        if (sigd->contentInfo.privateInfo->digcx == NULL)
    201            return SECFailure;
    202    }
    203    return SECSuccess;
    204 }
    205 
    206 /*
    207 * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
    208 *     after all the encapsulated data was passed through the encoder.
    209 *
    210 * In detail:
    211 *  - create the signatures in all the SignerInfos
    212 *
    213 * Please note that nothing is done to the Certificates and CRLs in the message - this
    214 * is entirely the responsibility of our callers.
    215 */
    216 SECStatus
    217 NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
    218 {
    219    NSSCMSSignerInfo **signerinfos, *signerinfo;
    220    NSSCMSContentInfo *cinfo;
    221    SECOidTag digestalgtag;
    222    SECStatus ret = SECFailure;
    223    SECStatus rv;
    224    SECItem *contentType;
    225    int certcount;
    226    int i, ci, cli, n, rci, si;
    227    PLArenaPool *poolp;
    228    CERTCertificateList *certlist;
    229    extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
    230 
    231    if (!sigd) {
    232        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    233        return SECFailure;
    234    }
    235 
    236    poolp = sigd->cmsg->poolp;
    237    cinfo = &(sigd->contentInfo);
    238 
    239    /* did we have digest calculation going on? */
    240    if (cinfo->privateInfo && cinfo->privateInfo->digcx) {
    241        rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp,
    242                                                 &(sigd->digests));
    243        /* error has been set by NSS_CMSDigestContext_FinishMultiple */
    244        cinfo->privateInfo->digcx = NULL;
    245        if (rv != SECSuccess)
    246            goto loser;
    247    }
    248 
    249    signerinfos = sigd->signerInfos;
    250    certcount = 0;
    251 
    252    /* prepare all the SignerInfos (there may be none) */
    253    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
    254        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
    255 
    256        /* find correct digest for this signerinfo */
    257        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    258        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
    259        if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
    260            /* oops - digest not found */
    261            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
    262            goto loser;
    263        }
    264 
    265        /* XXX if our content is anything else but data, we need to force the
    266         * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
    267         * collection...") */
    268 
    269        /* pass contentType here as we want a contentType attribute */
    270        if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
    271            goto loser;
    272 
    273        /* sign the thing */
    274        rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
    275        if (rv != SECSuccess)
    276            goto loser;
    277 
    278        /* while we're at it, count number of certs in certLists */
    279        certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
    280        if (certlist)
    281            certcount += certlist->len;
    282    }
    283 
    284    /* this is a SET OF, so we need to sort them guys */
    285    rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
    286    if (rv != SECSuccess)
    287        goto loser;
    288 
    289    /*
    290     * now prepare certs & crls
    291     */
    292 
    293    /* count the rest of the certs */
    294    if (sigd->certs != NULL) {
    295        for (ci = 0; sigd->certs[ci] != NULL; ci++)
    296            certcount++;
    297    }
    298 
    299    if (sigd->certLists != NULL) {
    300        for (cli = 0; sigd->certLists[cli] != NULL; cli++)
    301            certcount += sigd->certLists[cli]->len;
    302    }
    303 
    304    if (certcount == 0) {
    305        sigd->rawCerts = NULL;
    306    } else {
    307        /*
    308         * Combine all of the certs and cert chains into rawcerts.
    309         * Note: certcount is an upper bound; we may not need that many slots
    310         * but we will allocate anyway to avoid having to do another pass.
    311         * (The temporary space saving is not worth it.)
    312         *
    313         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
    314         *  SetOfDERcertficates implementation
    315         */
    316        sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
    317        if (sigd->rawCerts == NULL)
    318            return SECFailure;
    319 
    320        /*
    321         * XXX Want to check for duplicates and not add *any* cert that is
    322         * already in the set.  This will be more important when we start
    323         * dealing with larger sets of certs, dual-key certs (signing and
    324         * encryption), etc.  For the time being we can slide by...
    325         *
    326         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
    327         *  SetOfDERcertficates implementation
    328         */
    329        rci = 0;
    330        if (signerinfos != NULL) {
    331            for (si = 0; signerinfos[si] != NULL; si++) {
    332                signerinfo = signerinfos[si];
    333                for (ci = 0; ci < signerinfo->certList->len; ci++)
    334                    sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
    335            }
    336        }
    337 
    338        if (sigd->certs != NULL) {
    339            for (ci = 0; sigd->certs[ci] != NULL; ci++)
    340                sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
    341        }
    342 
    343        if (sigd->certLists != NULL) {
    344            for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
    345                for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
    346                    sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
    347            }
    348        }
    349 
    350        sigd->rawCerts[rci] = NULL;
    351 
    352        /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
    353        NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
    354    }
    355 
    356    ret = SECSuccess;
    357 
    358 loser:
    359    return ret;
    360 }
    361 
    362 SECStatus
    363 NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
    364 {
    365    SECStatus rv;
    366    if (!sigd) {
    367        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    368        return SECFailure;
    369    }
    370    rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
    371    if (rv != SECSuccess) {
    372        return SECFailure;
    373    }
    374    /* handle issue with Windows 2003 servers and kerberos */
    375    if (sigd->digestAlgorithms != NULL) {
    376        int i;
    377        for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
    378            SECAlgorithmID *algid = sigd->digestAlgorithms[i];
    379            SECOidTag senttag = SECOID_FindOIDTag(&algid->algorithm);
    380            SECOidTag maptag = NSS_CMSUtil_MapSignAlgs(senttag);
    381 
    382            if (maptag != senttag) {
    383                SECOidData *hashoid = SECOID_FindOIDByTag(maptag);
    384                rv = SECITEM_CopyItem(sigd->cmsg->poolp, &algid->algorithm, &hashoid->oid);
    385                if (rv != SECSuccess) {
    386                    return rv;
    387                }
    388            }
    389        }
    390    }
    391 
    392    /* set up the digests */
    393    if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
    394        /* if digests are already there, do nothing */
    395        sigd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
    396        if (sigd->contentInfo.privateInfo->digcx == NULL)
    397            return SECFailure;
    398    }
    399    return SECSuccess;
    400 }
    401 
    402 /*
    403 * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a
    404 *   SignedData after all the encapsulated data was passed through the decoder.
    405 */
    406 SECStatus
    407 NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
    408 {
    409    SECStatus rv = SECSuccess;
    410 
    411    if (!sigd) {
    412        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    413        return SECFailure;
    414    }
    415 
    416    /* did we have digest calculation going on? */
    417    if (sigd->contentInfo.privateInfo && sigd->contentInfo.privateInfo->digcx) {
    418        rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.privateInfo->digcx,
    419                                                 sigd->cmsg->poolp, &(sigd->digests));
    420        /* error set by NSS_CMSDigestContext_FinishMultiple */
    421        sigd->contentInfo.privateInfo->digcx = NULL;
    422    }
    423    return rv;
    424 }
    425 
    426 /*
    427 * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
    428 *     after all decoding is finished.
    429 */
    430 SECStatus
    431 NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
    432 {
    433    NSSCMSSignerInfo **signerinfos = NULL;
    434    int i;
    435 
    436    if (!sigd) {
    437        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    438        return SECFailure;
    439    }
    440 
    441    /* set cmsg for all the signerinfos */
    442    signerinfos = sigd->signerInfos;
    443 
    444    /* set cmsg for all the signerinfos */
    445    if (signerinfos) {
    446        for (i = 0; signerinfos[i] != NULL; i++)
    447            signerinfos[i]->cmsg = sigd->cmsg;
    448    }
    449 
    450    return SECSuccess;
    451 }
    452 
    453 /*
    454 * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
    455 */
    456 NSSCMSSignerInfo **
    457 NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
    458 {
    459    if (!sigd) {
    460        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    461        return NULL;
    462    }
    463    return sigd->signerInfos;
    464 }
    465 
    466 int
    467 NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
    468 {
    469    if (!sigd) {
    470        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    471        return 0;
    472    }
    473    return NSS_CMSArray_Count((void **)sigd->signerInfos);
    474 }
    475 
    476 NSSCMSSignerInfo *
    477 NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
    478 {
    479    if (!sigd || !sigd->signerInfos) {
    480        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    481        return NULL;
    482    }
    483    return sigd->signerInfos[i];
    484 }
    485 
    486 /*
    487 * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
    488 */
    489 SECAlgorithmID **
    490 NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
    491 {
    492    if (!sigd) {
    493        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    494        return NULL;
    495    }
    496    return sigd->digestAlgorithms;
    497 }
    498 
    499 /*
    500 * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
    501 */
    502 NSSCMSContentInfo *
    503 NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
    504 {
    505    if (!sigd) {
    506        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    507        return NULL;
    508    }
    509    return &(sigd->contentInfo);
    510 }
    511 
    512 /*
    513 * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
    514 */
    515 SECItem **
    516 NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
    517 {
    518    if (!sigd) {
    519        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    520        return NULL;
    521    }
    522    return sigd->rawCerts;
    523 }
    524 
    525 SECStatus
    526 NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
    527                              SECCertUsage certusage, PRBool keepcerts)
    528 {
    529    int certcount;
    530    CERTCertificate **certArray = NULL;
    531    CERTCertList *certList = NULL;
    532    CERTCertListNode *node;
    533    SECStatus rv;
    534    SECItem **rawArray;
    535    int i;
    536    PRTime now;
    537 
    538    if (!sigd) {
    539        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    540        return SECFailure;
    541    }
    542 
    543    certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
    544 
    545    /* get the certs in the temp DB */
    546    rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
    547                          &certArray, PR_FALSE, PR_FALSE, NULL);
    548    if (rv != SECSuccess) {
    549        goto loser;
    550    }
    551 
    552    /* save the certs so they don't get destroyed */
    553    for (i = 0; i < certcount; i++) {
    554        CERTCertificate *cert = certArray[i];
    555        if (cert)
    556            NSS_CMSSignedData_AddTempCertificate(sigd, cert);
    557    }
    558 
    559    if (!keepcerts) {
    560        goto done;
    561    }
    562 
    563    /* build a CertList for filtering */
    564    certList = CERT_NewCertList();
    565    if (certList == NULL) {
    566        rv = SECFailure;
    567        goto loser;
    568    }
    569    for (i = 0; i < certcount; i++) {
    570        CERTCertificate *cert = certArray[i];
    571        if (cert)
    572            cert = CERT_DupCertificate(cert);
    573        if (cert)
    574            CERT_AddCertToListTail(certList, cert);
    575    }
    576 
    577    /* filter out the certs we don't want */
    578    rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
    579    if (rv != SECSuccess) {
    580        goto loser;
    581    }
    582 
    583    /* go down the remaining list of certs and verify that they have
    584     * valid chains, then import them.
    585     */
    586    now = PR_Now();
    587    for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
    588         node = CERT_LIST_NEXT(node)) {
    589        CERTCertificateList *certChain;
    590 
    591        if (CERT_VerifyCert(certdb, node->cert,
    592                            PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
    593            continue;
    594        }
    595 
    596        certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
    597        if (!certChain) {
    598            continue;
    599        }
    600 
    601        /*
    602         * CertChain returns an array of SECItems, import expects an array of
    603         * SECItem pointers. Create the SECItem Pointers from the array of
    604         * SECItems.
    605         */
    606        rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *));
    607        if (!rawArray) {
    608            CERT_DestroyCertificateList(certChain);
    609            continue;
    610        }
    611        for (i = 0; i < certChain->len; i++) {
    612            rawArray[i] = &certChain->certs[i];
    613        }
    614        (void)CERT_ImportCerts(certdb, certusage, certChain->len,
    615                               rawArray, NULL, keepcerts, PR_FALSE, NULL);
    616        PORT_Free(rawArray);
    617        CERT_DestroyCertificateList(certChain);
    618    }
    619 
    620    rv = SECSuccess;
    621 
    622    /* XXX CRL handling */
    623 
    624 done:
    625    if (sigd->signerInfos != NULL) {
    626        /* fill in all signerinfo's certs */
    627        for (i = 0; sigd->signerInfos[i] != NULL; i++)
    628            (void)NSS_CMSSignerInfo_GetSigningCertificate(
    629                sigd->signerInfos[i], certdb);
    630    }
    631 
    632 loser:
    633    /* now free everything */
    634    if (certArray) {
    635        CERT_DestroyCertArray(certArray, certcount);
    636    }
    637    if (certList) {
    638        CERT_DestroyCertList(certList);
    639    }
    640 
    641    return rv;
    642 }
    643 
    644 /*
    645 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
    646 *     of external signatures!
    647 */
    648 
    649 /*
    650 * NSS_CMSSignedData_VerifySignerInfo - check the signatures.
    651 *
    652 * The digests were either calculated during decoding (and are stored in the
    653 * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
    654 *
    655 * The verification checks if the signing cert is valid and has a trusted chain
    656 * for the purpose specified by "certusage".
    657 */
    658 SECStatus
    659 NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i,
    660                                   CERTCertDBHandle *certdb, SECCertUsage certusage)
    661 {
    662    NSSCMSSignerInfo *signerinfo;
    663    NSSCMSContentInfo *cinfo;
    664    SECOidData *algiddata;
    665    SECItem *contentType, *digest;
    666    SECOidTag oidTag;
    667    SECStatus rv;
    668 
    669    if (!sigd || !sigd->signerInfos) {
    670        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    671        return SECFailure;
    672    }
    673 
    674    cinfo = &(sigd->contentInfo);
    675 
    676    signerinfo = sigd->signerInfos[i];
    677 
    678    /* verify certificate */
    679    rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage);
    680    if (rv != SECSuccess)
    681        return rv; /* error is set */
    682 
    683    /* find digest and contentType for signerinfo */
    684    algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
    685    oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN;
    686    digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag);
    687    /* NULL digest is acceptable. */
    688    contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
    689    /* NULL contentType is acceptable. */
    690 
    691    /* now verify signature */
    692    rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
    693    return rv;
    694 }
    695 
    696 /*
    697 * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
    698 */
    699 SECStatus
    700 NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd,
    701                                  CERTCertDBHandle *certdb,
    702                                  SECCertUsage usage)
    703 {
    704    CERTCertificate *cert;
    705    SECStatus rv = SECSuccess;
    706    int i;
    707    int count;
    708    PRTime now;
    709    void *pwarg = NULL;
    710 
    711    if (!sigd || !certdb || !sigd->rawCerts) {
    712        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    713        return SECFailure;
    714    }
    715 
    716    count = NSS_CMSArray_Count((void **)sigd->rawCerts);
    717    now = PR_Now();
    718    for (i = 0; i < count; i++) {
    719        if (sigd->certs && sigd->certs[i]) {
    720            cert = CERT_DupCertificate(sigd->certs[i]);
    721        } else {
    722            cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]);
    723            if (!cert) {
    724                rv = SECFailure;
    725                break;
    726            }
    727        }
    728        if (sigd->cmsg) {
    729            pwarg = sigd->cmsg->pwfn_arg;
    730        }
    731        rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now,
    732                              pwarg, NULL);
    733        CERT_DestroyCertificate(cert);
    734    }
    735 
    736    return rv;
    737 }
    738 
    739 /*
    740 * NSS_CMSSignedData_HasDigests - see if we have digests in place
    741 */
    742 PRBool
    743 NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
    744 {
    745    if (!sigd) {
    746        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    747        return PR_FALSE;
    748    }
    749    return (sigd->digests != NULL);
    750 }
    751 
    752 SECStatus
    753 NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
    754 {
    755    SECStatus rv;
    756 
    757    if (!sigd || !certlist) {
    758        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    759        return SECFailure;
    760    }
    761 
    762    /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
    763    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
    764 
    765    return rv;
    766 }
    767 
    768 /*
    769 * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
    770 */
    771 SECStatus
    772 NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
    773 {
    774    CERTCertificateList *certlist;
    775    SECCertUsage usage;
    776    SECStatus rv;
    777 
    778    usage = certUsageEmailSigner;
    779 
    780    if (!sigd || !cert) {
    781        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    782        return SECFailure;
    783    }
    784 
    785    /* do not include root */
    786    certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
    787    if (certlist == NULL)
    788        return SECFailure;
    789 
    790    rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
    791 
    792    return rv;
    793 }
    794 
    795 extern SECStatus
    796 NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
    797 {
    798    CERTCertificate *c;
    799    SECStatus rv;
    800 
    801    if (!sigd || !cert) {
    802        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    803        return SECFailure;
    804    }
    805 
    806    c = CERT_DupCertificate(cert);
    807    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c);
    808    return rv;
    809 }
    810 
    811 SECStatus
    812 NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
    813 {
    814    CERTCertificate *c;
    815    SECStatus rv;
    816 
    817    if (!sigd || !cert) {
    818        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    819        return SECFailure;
    820    }
    821 
    822    c = CERT_DupCertificate(cert);
    823    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
    824    return rv;
    825 }
    826 
    827 PRBool
    828 NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
    829 {
    830    if (!sigd) {
    831        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    832        return PR_FALSE;
    833    }
    834    if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
    835        return PR_TRUE;
    836    else if (sigd->crls != NULL && sigd->crls[0] != NULL)
    837        return PR_TRUE;
    838    else
    839        return PR_FALSE;
    840 }
    841 
    842 SECStatus
    843 NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
    844                                NSSCMSSignerInfo *signerinfo)
    845 {
    846    void *mark;
    847    SECStatus rv;
    848    SECOidTag digestalgtag;
    849    PLArenaPool *poolp;
    850 
    851    if (!sigd || !signerinfo) {
    852        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    853        return SECFailure;
    854    }
    855 
    856    poolp = sigd->cmsg->poolp;
    857 
    858    mark = PORT_ArenaMark(poolp);
    859 
    860    /* add signerinfo */
    861    rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
    862    if (rv != SECSuccess)
    863        goto loser;
    864 
    865    /*
    866     * add empty digest
    867     * Empty because we don't have it yet. Either it gets created during encoding
    868     * (if the data is present) or has to be set externally.
    869     * XXX maybe pass it in optionally?
    870     */
    871    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    872    rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
    873    if (rv != SECSuccess)
    874        goto loser;
    875 
    876    /*
    877     * The last thing to get consistency would be adding the digest.
    878     */
    879 
    880    PORT_ArenaUnmark(poolp, mark);
    881    return SECSuccess;
    882 
    883 loser:
    884    PORT_ArenaRelease(poolp, mark);
    885    return SECFailure;
    886 }
    887 
    888 /*
    889 * NSS_CMSSignedData_SetDigests - set a signedData's digests member
    890 *
    891 * "digestalgs" - array of digest algorithm IDs
    892 * "digests"    - array of digests corresponding to the digest algorithms
    893 */
    894 SECStatus
    895 NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
    896                             SECAlgorithmID **digestalgs,
    897                             SECItem **digests)
    898 {
    899    int cnt, i, idx;
    900 
    901    if (!sigd || !digestalgs || !digests) {
    902        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    903        return SECFailure;
    904    }
    905 
    906    if (sigd->digestAlgorithms == NULL) {
    907        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    908        return SECFailure;
    909    }
    910 
    911    /* we assume that the digests array is just not there yet */
    912    PORT_Assert(sigd->digests == NULL);
    913    if (sigd->digests != NULL) {
    914        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    915        return SECFailure;
    916    }
    917 
    918    /* now allocate one (same size as digestAlgorithms) */
    919    cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
    920    sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
    921    if (sigd->digests == NULL) {
    922        PORT_SetError(SEC_ERROR_NO_MEMORY);
    923        return SECFailure;
    924    }
    925 
    926    for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
    927        /* try to find the sigd's i'th digest algorithm in the array we passed in */
    928        idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
    929        if (idx < 0) {
    930            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
    931            return SECFailure;
    932        }
    933        if (!digests[idx]) {
    934            /* We have no digest for this algorithm, probably because it is
    935            ** unrecognized or unsupported.  We'll ignore this here.  If this
    936            ** digest is needed later, an error will be be generated then.
    937            */
    938            continue;
    939        }
    940 
    941        /* found it - now set it */
    942        if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
    943            SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) {
    944            PORT_SetError(SEC_ERROR_NO_MEMORY);
    945            return SECFailure;
    946        }
    947    }
    948    return SECSuccess;
    949 }
    950 
    951 SECStatus
    952 NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
    953                                 SECOidTag digestalgtag,
    954                                 SECItem *digestdata)
    955 {
    956    SECItem *digest = NULL;
    957    PLArenaPool *poolp;
    958    void *mark;
    959    int n, cnt;
    960 
    961    if (!sigd) {
    962        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    963        return SECFailure;
    964    }
    965 
    966    poolp = sigd->cmsg->poolp;
    967 
    968    mark = PORT_ArenaMark(poolp);
    969 
    970    if (digestdata) {
    971        digest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
    972 
    973        /* copy digestdata item to arena (in case we have it and are not only making room) */
    974        if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
    975            goto loser;
    976    }
    977 
    978    /* now allocate one (same size as digestAlgorithms) */
    979    if (sigd->digests == NULL) {
    980        cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
    981        sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
    982        if (sigd->digests == NULL) {
    983            PORT_SetError(SEC_ERROR_NO_MEMORY);
    984            return SECFailure;
    985        }
    986    }
    987 
    988    n = -1;
    989    if (sigd->digestAlgorithms != NULL)
    990        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
    991 
    992    /* if not found, add a digest */
    993    if (n < 0) {
    994        if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
    995            goto loser;
    996    } else {
    997        /* replace NULL pointer with digest item (and leak previous value) */
    998        sigd->digests[n] = digest;
    999    }
   1000 
   1001    PORT_ArenaUnmark(poolp, mark);
   1002    return SECSuccess;
   1003 
   1004 loser:
   1005    PORT_ArenaRelease(poolp, mark);
   1006    return SECFailure;
   1007 }
   1008 
   1009 SECStatus
   1010 NSS_CMSSignedData_AddDigest(PLArenaPool *poolp,
   1011                            NSSCMSSignedData *sigd,
   1012                            SECOidTag digestalgtag,
   1013                            SECItem *digest)
   1014 {
   1015    SECAlgorithmID *digestalg;
   1016    void *mark;
   1017 
   1018    if (!sigd || !poolp) {
   1019        PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1020        return SECFailure;
   1021    }
   1022 
   1023    mark = PORT_ArenaMark(poolp);
   1024 
   1025    digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
   1026    if (digestalg == NULL)
   1027        goto loser;
   1028 
   1029    if (SECOID_SetAlgorithmID(poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
   1030        goto loser;
   1031 
   1032    if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms),
   1033                         (void *)digestalg) != SECSuccess ||
   1034        /* even if digest is NULL, add dummy to have same-size array */
   1035        NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests),
   1036                         (void *)digest) != SECSuccess) {
   1037        goto loser;
   1038    }
   1039 
   1040    PORT_ArenaUnmark(poolp, mark);
   1041    return SECSuccess;
   1042 
   1043 loser:
   1044    PORT_ArenaRelease(poolp, mark);
   1045    return SECFailure;
   1046 }
   1047 
   1048 /* XXX This function doesn't set the error code on failure. */
   1049 SECItem *
   1050 NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
   1051 {
   1052    int n;
   1053 
   1054    if (!sigd) {
   1055        PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1056        return NULL;
   1057    }
   1058 
   1059    if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) {
   1060        PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
   1061        return NULL;
   1062    }
   1063 
   1064    n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
   1065 
   1066    return (n < 0) ? NULL : sigd->digests[n];
   1067 }
   1068 
   1069 /* =============================================================================
   1070 * Misc. utility functions
   1071 */
   1072 
   1073 /*
   1074 * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
   1075 *
   1076 * cert          - base certificates that will be included
   1077 * include_chain - if true, include the complete cert chain for cert
   1078 *
   1079 * More certs and chains can be added via AddCertificate and AddCertChain.
   1080 *
   1081 * An error results in a return value of NULL and an error set.
   1082 *
   1083 * XXXX CRLs
   1084 */
   1085 NSSCMSSignedData *
   1086 NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
   1087 {
   1088    NSSCMSSignedData *sigd;
   1089    void *mark;
   1090    PLArenaPool *poolp;
   1091    SECStatus rv;
   1092 
   1093    if (!cmsg || !cert) {
   1094        PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1095        return NULL;
   1096    }
   1097 
   1098    poolp = cmsg->poolp;
   1099    mark = PORT_ArenaMark(poolp);
   1100 
   1101    sigd = NSS_CMSSignedData_Create(cmsg);
   1102    if (sigd == NULL)
   1103        goto loser;
   1104 
   1105    /* no signerinfos, thus no digestAlgorithms */
   1106 
   1107    /* but certs */
   1108    if (include_chain) {
   1109        rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
   1110    } else {
   1111        rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
   1112    }
   1113    if (rv != SECSuccess)
   1114        goto loser;
   1115 
   1116    /* RFC2630 5.2 sez:
   1117     * In the degenerate case where there are no signers, the
   1118     * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
   1119     * case, the content type within the EncapsulatedContentInfo value being
   1120     * "signed" should be id-data (as defined in section 4), and the content
   1121     * field of the EncapsulatedContentInfo value should be omitted.
   1122     */
   1123    rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
   1124    if (rv != SECSuccess)
   1125        goto loser;
   1126 
   1127    PORT_ArenaUnmark(poolp, mark);
   1128    return sigd;
   1129 
   1130 loser:
   1131    if (sigd)
   1132        NSS_CMSSignedData_Destroy(sigd);
   1133    PORT_ArenaRelease(poolp, mark);
   1134    return NULL;
   1135 }
   1136 
   1137 /* TODO:
   1138 * NSS_CMSSignerInfo_GetReceiptRequest()
   1139 * NSS_CMSSignedData_HasReceiptRequest()
   1140 * easy way to iterate over signers
   1141 */