Back to index

lightning-sunbird  0.9+nobinonly
cmssigdata.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * CMS signedData methods.
00039  *
00040  * $Id: cmssigdata.c,v 1.29 2005/06/27 22:21:18 julien.pierre.bugs%sun.com Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "cert.h"
00046 /*#include "cdbhdl.h"*/
00047 #include "secasn1.h"
00048 #include "secitem.h"
00049 #include "secoid.h"
00050 #include "pk11func.h"
00051 #include "secerr.h"
00052 
00053 NSSCMSSignedData *
00054 NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
00055 {
00056     void *mark;
00057     NSSCMSSignedData *sigd;
00058     PLArenaPool *poolp;
00059 
00060     if (!cmsg) {
00061         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00062         return NULL;
00063     }
00064 
00065     poolp = cmsg->poolp;
00066 
00067     mark = PORT_ArenaMark(poolp);
00068 
00069     sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSSignedData));
00070     if (sigd == NULL)
00071        goto loser;
00072 
00073     sigd->cmsg = cmsg;
00074 
00075     /* signerInfos, certs, certlists, crls are all empty */
00076     /* version is set in NSS_CMSSignedData_Finalize() */
00077 
00078     PORT_ArenaUnmark(poolp, mark);
00079     return sigd;
00080 
00081 loser:
00082     PORT_ArenaRelease(poolp, mark);
00083     return NULL;
00084 }
00085 
00086 void
00087 NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
00088 {
00089     CERTCertificate **certs, **tempCerts, *cert;
00090     CERTCertificateList **certlists, *certlist;
00091     NSSCMSSignerInfo **signerinfos, *si;
00092 
00093     if (sigd == NULL)
00094        return;
00095 
00096     certs = sigd->certs;
00097     tempCerts = sigd->tempCerts;
00098     certlists = sigd->certLists;
00099     signerinfos = sigd->signerInfos;
00100 
00101     if (certs != NULL) {
00102        while ((cert = *certs++) != NULL)
00103            CERT_DestroyCertificate (cert);
00104     }
00105 
00106     if (tempCerts != NULL) {
00107        while ((cert = *tempCerts++) != NULL)
00108            CERT_DestroyCertificate (cert);
00109     }
00110 
00111     if (certlists != NULL) {
00112        while ((certlist = *certlists++) != NULL)
00113            CERT_DestroyCertificateList (certlist);
00114     }
00115 
00116     if (signerinfos != NULL) {
00117        while ((si = *signerinfos++) != NULL)
00118            NSS_CMSSignerInfo_Destroy(si);
00119     }
00120 
00121     /* everything's in a pool, so don't worry about the storage */
00122    NSS_CMSContentInfo_Destroy(&(sigd->contentInfo));
00123 
00124 }
00125 
00126 /*
00127  * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
00128  *     before start of encoding.
00129  *
00130  * In detail:
00131  *  - find out about the right value to put into sigd->version
00132  *  - come up with a list of digestAlgorithms (which should be the union of the algorithms
00133  *         in the signerinfos).
00134  *         If we happen to have a pre-set list of algorithms (and digest values!), we
00135  *         check if we have all the signerinfos' algorithms. If not, this is an error.
00136  */
00137 SECStatus
00138 NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
00139 {
00140     NSSCMSSignerInfo *signerinfo;
00141     SECOidTag digestalgtag;
00142     SECItem *dummy;
00143     int version;
00144     SECStatus rv;
00145     PRBool haveDigests = PR_FALSE;
00146     int n, i;
00147     PLArenaPool *poolp;
00148 
00149     if (!sigd) {
00150         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00151         return SECFailure;
00152     }
00153 
00154     poolp = sigd->cmsg->poolp;
00155 
00156     /* we assume that we have precomputed digests if there is a list of algorithms, and */
00157     /* a chunk of data for each of those algorithms */
00158     if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
00159        for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
00160            if (sigd->digests[i] == NULL)
00161               break;
00162        }
00163        if (sigd->digestAlgorithms[i] == NULL)    /* reached the end of the array? */
00164            haveDigests = PR_TRUE;         /* yes: we must have all the digests */
00165     }
00166            
00167     version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
00168 
00169     /* RFC2630 5.1 "version is the syntax version number..." */
00170     if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
00171        version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
00172 
00173     /* prepare all the SignerInfos (there may be none) */
00174     for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
00175        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
00176 
00177        /* RFC2630 5.1 "version is the syntax version number..." */
00178        if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
00179            version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
00180        
00181        /* collect digestAlgorithms from SignerInfos */
00182        /* (we need to know which algorithms we have when the content comes in) */
00183        /* do not overwrite any existing digestAlgorithms (and digest) */
00184        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
00185        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
00186        if (n < 0 && haveDigests) {
00187            /* oops, there is a digestalg we do not have a digest for */
00188            /* but we were supposed to have all the digests already... */
00189            goto loser;
00190        } else if (n < 0) {
00191            /* add the digestAlgorithm & a NULL digest */
00192            rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
00193            if (rv != SECSuccess)
00194               goto loser;
00195        } else {
00196            /* found it, nothing to do */
00197        }
00198     }
00199 
00200     dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
00201     if (dummy == NULL)
00202        return SECFailure;
00203 
00204     /* this is a SET OF, so we need to sort them guys */
00205     rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms, 
00206                                 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
00207                             (void **)sigd->digests);
00208     if (rv != SECSuccess)
00209        return SECFailure;
00210     
00211     return SECSuccess;
00212 
00213 loser:
00214     return SECFailure;
00215 }
00216 
00217 SECStatus
00218 NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
00219 {
00220     if (!sigd) {
00221         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00222         return SECFailure;
00223     }
00224     /* set up the digests */
00225     if (sigd->digests && sigd->digests[0]) {
00226        sigd->contentInfo.digcx = NULL; /* don't attempt to make new ones. */
00227     } else if (sigd->digestAlgorithms != NULL) {
00228        sigd->contentInfo.digcx = 
00229                NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
00230        if (sigd->contentInfo.digcx == NULL)
00231            return SECFailure;
00232     }
00233     return SECSuccess;
00234 }
00235 
00236 /*
00237  * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
00238  *     after all the encapsulated data was passed through the encoder.
00239  *
00240  * In detail:
00241  *  - create the signatures in all the SignerInfos
00242  *
00243  * Please note that nothing is done to the Certificates and CRLs in the message - this
00244  * is entirely the responsibility of our callers.
00245  */
00246 SECStatus
00247 NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
00248 {
00249     NSSCMSSignerInfo **signerinfos, *signerinfo;
00250     NSSCMSContentInfo *cinfo;
00251     SECOidTag digestalgtag;
00252     SECStatus ret = SECFailure;
00253     SECStatus rv;
00254     SECItem *contentType;
00255     int certcount;
00256     int i, ci, cli, n, rci, si;
00257     PLArenaPool *poolp;
00258     CERTCertificateList *certlist;
00259     extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
00260 
00261     if (!sigd) {
00262         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00263         return SECFailure;
00264     }
00265 
00266     poolp = sigd->cmsg->poolp;
00267     cinfo = &(sigd->contentInfo);
00268 
00269     /* did we have digest calculation going on? */
00270     if (cinfo->digcx) {
00271        rv = NSS_CMSDigestContext_FinishMultiple(cinfo->digcx, poolp, 
00272                                                 &(sigd->digests));
00273        /* error has been set by NSS_CMSDigestContext_FinishMultiple */
00274        cinfo->digcx = NULL;
00275        if (rv != SECSuccess)
00276            goto loser;             
00277     }
00278 
00279     signerinfos = sigd->signerInfos;
00280     certcount = 0;
00281 
00282     /* prepare all the SignerInfos (there may be none) */
00283     for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
00284        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
00285 
00286        /* find correct digest for this signerinfo */
00287        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
00288        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
00289        if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
00290            /* oops - digest not found */
00291            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
00292            goto loser;
00293        }
00294 
00295        /* XXX if our content is anything else but data, we need to force the
00296         * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
00297         * collection...") */
00298 
00299        /* pass contentType here as we want a contentType attribute */
00300        if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
00301            goto loser;
00302 
00303        /* sign the thing */
00304        rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
00305        if (rv != SECSuccess)
00306            goto loser;
00307 
00308        /* while we're at it, count number of certs in certLists */
00309        certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
00310        if (certlist)
00311            certcount += certlist->len;
00312     }
00313 
00314     /* this is a SET OF, so we need to sort them guys */
00315     rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
00316     if (rv != SECSuccess)
00317        goto loser;
00318 
00319     /*
00320      * now prepare certs & crls
00321      */
00322 
00323     /* count the rest of the certs */
00324     if (sigd->certs != NULL) {
00325        for (ci = 0; sigd->certs[ci] != NULL; ci++)
00326            certcount++;
00327     }
00328 
00329     if (sigd->certLists != NULL) {
00330        for (cli = 0; sigd->certLists[cli] != NULL; cli++)
00331            certcount += sigd->certLists[cli]->len;
00332     }
00333 
00334     if (certcount == 0) {
00335        sigd->rawCerts = NULL;
00336     } else {
00337        /*
00338         * Combine all of the certs and cert chains into rawcerts.
00339         * Note: certcount is an upper bound; we may not need that many slots
00340         * but we will allocate anyway to avoid having to do another pass.
00341         * (The temporary space saving is not worth it.)
00342         *
00343         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
00344         *  SetOfDERcertficates implementation
00345         */
00346        sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
00347        if (sigd->rawCerts == NULL)
00348            return SECFailure;
00349 
00350        /*
00351         * XXX Want to check for duplicates and not add *any* cert that is
00352         * already in the set.  This will be more important when we start
00353         * dealing with larger sets of certs, dual-key certs (signing and
00354         * encryption), etc.  For the time being we can slide by...
00355         *
00356         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
00357         *  SetOfDERcertficates implementation
00358         */
00359        rci = 0;
00360        if (signerinfos != NULL) {
00361            for (si = 0; signerinfos[si] != NULL; si++) {
00362               signerinfo = signerinfos[si];
00363               for (ci = 0; ci < signerinfo->certList->len; ci++)
00364                   sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
00365            }
00366        }
00367 
00368        if (sigd->certs != NULL) {
00369            for (ci = 0; sigd->certs[ci] != NULL; ci++)
00370               sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
00371        }
00372 
00373        if (sigd->certLists != NULL) {
00374            for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
00375               for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
00376                   sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
00377            }
00378        }
00379 
00380        sigd->rawCerts[rci] = NULL;
00381 
00382        /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
00383        NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
00384     }
00385 
00386     ret = SECSuccess;
00387 
00388 loser:
00389     return ret;
00390 }
00391 
00392 SECStatus
00393 NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
00394 {
00395     if (!sigd) {
00396         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00397         return SECFailure;
00398     }
00399     /* set up the digests */
00400     if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
00401        /* if digests are already there, do nothing */
00402        sigd->contentInfo.digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
00403        if (sigd->contentInfo.digcx == NULL)
00404            return SECFailure;
00405     }
00406     return SECSuccess;
00407 }
00408 
00409 /*
00410  * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a 
00411  *   SignedData after all the encapsulated data was passed through the decoder.
00412  */
00413 SECStatus
00414 NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
00415 {
00416     SECStatus rv = SECSuccess;
00417 
00418     if (!sigd) {
00419         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00420         return SECFailure;
00421     }
00422 
00423     /* did we have digest calculation going on? */
00424     if (sigd->contentInfo.digcx) {
00425        rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.digcx, 
00426                                    sigd->cmsg->poolp, &(sigd->digests));
00427        /* error set by NSS_CMSDigestContext_FinishMultiple */
00428        sigd->contentInfo.digcx = NULL;
00429     }
00430     return rv;
00431 }
00432 
00433 /*
00434  * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
00435  *     after all decoding is finished.
00436  */
00437 SECStatus
00438 NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
00439 {
00440     NSSCMSSignerInfo **signerinfos = NULL;
00441     int i;
00442 
00443     if (!sigd) {
00444         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00445         return SECFailure;
00446     }
00447 
00448     /* set cmsg for all the signerinfos */
00449     signerinfos = sigd->signerInfos;
00450 
00451     /* set cmsg for all the signerinfos */
00452     if (signerinfos) {
00453        for (i = 0; signerinfos[i] != NULL; i++)
00454            signerinfos[i]->cmsg = sigd->cmsg;
00455     }
00456 
00457     return SECSuccess;
00458 }
00459 
00460 /* 
00461  * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
00462  */
00463 NSSCMSSignerInfo **
00464 NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
00465 {
00466     if (!sigd) {
00467         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00468         return NULL;
00469     }
00470     return sigd->signerInfos;
00471 }
00472 
00473 int
00474 NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
00475 {
00476     if (!sigd) {
00477         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00478         return 0;
00479     }
00480     return NSS_CMSArray_Count((void **)sigd->signerInfos);
00481 }
00482 
00483 NSSCMSSignerInfo *
00484 NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
00485 {
00486     if (!sigd) {
00487         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00488         return NULL;
00489     }
00490     return sigd->signerInfos[i];
00491 }
00492 
00493 /* 
00494  * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
00495  */
00496 SECAlgorithmID **
00497 NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
00498 {
00499     if (!sigd) {
00500         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00501         return NULL;
00502     }
00503     return sigd->digestAlgorithms;
00504 }
00505 
00506 /*
00507  * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
00508  */
00509 NSSCMSContentInfo *
00510 NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
00511 {
00512     if (!sigd) {
00513         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00514         return NULL;
00515     }
00516     return &(sigd->contentInfo);
00517 }
00518 
00519 /* 
00520  * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
00521  */
00522 SECItem **
00523 NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
00524 {
00525     if (!sigd) {
00526         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00527         return NULL;
00528     }
00529     return sigd->rawCerts;
00530 }
00531 
00532 SECStatus
00533 NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
00534                             SECCertUsage certusage, PRBool keepcerts)
00535 {
00536     int certcount;
00537     CERTCertificate **certArray = NULL;
00538     CERTCertList *certList = NULL;
00539     CERTCertListNode *node;
00540     SECStatus rv;
00541     SECItem **rawArray;
00542     int i;
00543     PRTime now;
00544 
00545     if (!sigd) {
00546         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00547         return SECFailure;
00548     }
00549 
00550     certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
00551 
00552     /* get the certs in the temp DB */
00553     rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, 
00554                       &certArray, PR_FALSE, PR_FALSE, NULL);
00555     if (rv != SECSuccess) {
00556        goto loser;
00557     }
00558 
00559     /* save the certs so they don't get destroyed */
00560     for (i=0; i < certcount; i++) {
00561        CERTCertificate *cert = certArray[i];
00562        if (cert)
00563             NSS_CMSSignedData_AddTempCertificate(sigd, cert);
00564     }
00565 
00566     if (!keepcerts) {
00567        goto done;
00568     }
00569 
00570     /* build a CertList for filtering */
00571     certList = CERT_NewCertList();
00572     if (certList == NULL) {
00573        rv = SECFailure;
00574        goto loser;
00575     }
00576     for (i=0; i < certcount; i++) {
00577        CERTCertificate *cert = certArray[i];
00578        if (cert)
00579            cert = CERT_DupCertificate(cert);
00580        if (cert)
00581            CERT_AddCertToListTail(certList,cert);
00582     }
00583 
00584     /* filter out the certs we don't want */
00585     rv = CERT_FilterCertListByUsage(certList,certusage, PR_FALSE);
00586     if (rv != SECSuccess) {
00587        goto loser;
00588     }
00589 
00590     /* go down the remaining list of certs and verify that they have
00591      * valid chains, then import them.
00592      */
00593     now = PR_Now();
00594     for (node = CERT_LIST_HEAD(certList) ; !CERT_LIST_END(node,certList);
00595                                           node= CERT_LIST_NEXT(node)) {
00596        CERTCertificateList *certChain;
00597 
00598        if (CERT_VerifyCert(certdb, node->cert, 
00599               PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
00600            continue;
00601        }
00602 
00603        certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
00604        if (!certChain) {
00605            continue;
00606        }
00607 
00608        /*
00609         * CertChain returns an array of SECItems, import expects an array of
00610         * SECItem pointers. Create the SECItem Pointers from the array of
00611         * SECItems.
00612         */
00613        rawArray = (SECItem **)PORT_Alloc(certChain->len*sizeof (SECItem *));
00614        if (!rawArray) {
00615            CERT_DestroyCertificateList(certChain);
00616            continue;
00617        }
00618        for (i=0; i < certChain->len; i++) {
00619            rawArray[i] = &certChain->certs[i];
00620        }
00621        (void )CERT_ImportCerts(certdb, certusage, certChain->len, 
00622                      rawArray,  NULL, keepcerts, PR_FALSE, NULL);
00623        PORT_Free(rawArray);
00624        CERT_DestroyCertificateList(certChain);
00625     }
00626 
00627     rv = SECSuccess;
00628 
00629     /* XXX CRL handling */
00630 
00631 done:
00632     if (sigd->signerInfos != NULL) {
00633        /* fill in all signerinfo's certs */
00634        for (i = 0; sigd->signerInfos[i] != NULL; i++)
00635            (void)NSS_CMSSignerInfo_GetSigningCertificate(
00636                                           sigd->signerInfos[i], certdb);
00637     }
00638 
00639 loser:
00640     /* now free everything */
00641     if (certArray) {
00642        CERT_DestroyCertArray(certArray,certcount);
00643     }
00644     if (certList) {
00645        CERT_DestroyCertList(certList);
00646     }
00647 
00648     return rv;
00649 }
00650 
00651 /*
00652  * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
00653  *     of external signatures!
00654  */
00655 
00656 /*
00657  * NSS_CMSSignedData_VerifySignerInfo - check the signatures.
00658  *
00659  * The digests were either calculated during decoding (and are stored in the
00660  * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
00661  *
00662  * The verification checks if the signing cert is valid and has a trusted chain
00663  * for the purpose specified by "certusage".
00664  */
00665 SECStatus
00666 NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i, 
00667                          CERTCertDBHandle *certdb, SECCertUsage certusage)
00668 {
00669     NSSCMSSignerInfo *signerinfo;
00670     NSSCMSContentInfo *cinfo;
00671     SECOidData *algiddata;
00672     SECItem *contentType, *digest;
00673     SECOidTag oidTag;
00674     SECStatus rv;
00675 
00676     if (!sigd) {
00677         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00678         return SECFailure;
00679     }
00680 
00681     cinfo = &(sigd->contentInfo);
00682 
00683     signerinfo = sigd->signerInfos[i];
00684 
00685     /* verify certificate */
00686     rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage);
00687     if (rv != SECSuccess)
00688        return rv; /* error is set */
00689 
00690     /* find digest and contentType for signerinfo */
00691     algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
00692     oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN;
00693     digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag);
00694     /* NULL digest is acceptable. */
00695     contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
00696     /* NULL contentType is acceptable. */
00697 
00698     /* now verify signature */
00699     rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
00700     return rv;
00701 }
00702 
00703 /*
00704  * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
00705  */
00706 SECStatus
00707 NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd, 
00708                                   CERTCertDBHandle *certdb, 
00709                                   SECCertUsage usage)
00710 {
00711     CERTCertificate *cert;
00712     SECStatus rv = SECSuccess;
00713     int i;
00714     int count;
00715     PRTime now;
00716 
00717     if (!sigd || !certdb || !sigd->rawCerts) {
00718        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00719        return SECFailure;
00720     }
00721 
00722     count = NSS_CMSArray_Count((void**)sigd->rawCerts);
00723     now = PR_Now();
00724     for (i=0; i < count; i++) {
00725        if (sigd->certs && sigd->certs[i]) {
00726            cert = CERT_DupCertificate(sigd->certs[i]);
00727        } else {
00728            cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]);
00729            if (!cert) {
00730               rv = SECFailure;
00731               break;
00732            }
00733        }
00734        rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now, 
00735                               NULL, NULL);
00736        CERT_DestroyCertificate(cert);
00737     }
00738 
00739     return rv;
00740 }
00741 
00742 /*
00743  * NSS_CMSSignedData_HasDigests - see if we have digests in place
00744  */
00745 PRBool
00746 NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
00747 {
00748     if (!sigd) {
00749         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00750         return PR_FALSE;
00751     }
00752     return (sigd->digests != NULL);
00753 }
00754 
00755 SECStatus
00756 NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
00757 {
00758     SECStatus rv;
00759 
00760     if (!sigd || !certlist) {
00761         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00762         return SECFailure;
00763     }
00764 
00765     /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
00766     rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
00767 
00768     return rv;
00769 }
00770 
00771 /*
00772  * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs 
00773  */
00774 SECStatus
00775 NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
00776 {
00777     CERTCertificateList *certlist;
00778     SECCertUsage usage;
00779     SECStatus rv;
00780 
00781     usage = certUsageEmailSigner;
00782 
00783     if (!sigd || !cert) {
00784         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00785         return SECFailure;
00786     }
00787 
00788     /* do not include root */
00789     certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
00790     if (certlist == NULL)
00791        return SECFailure;
00792 
00793     rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
00794 
00795     return rv;
00796 }
00797 
00798 extern SECStatus
00799 NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
00800 {
00801     CERTCertificate *c;
00802     SECStatus rv;
00803 
00804     if (!sigd || !cert) {
00805         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00806         return SECFailure;
00807     }
00808 
00809     c = CERT_DupCertificate(cert);
00810     rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c);
00811     return rv;
00812 }
00813 
00814 SECStatus
00815 NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
00816 {
00817     CERTCertificate *c;
00818     SECStatus rv;
00819 
00820     if (!sigd || !cert) {
00821         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00822         return SECFailure;
00823     }
00824 
00825     c = CERT_DupCertificate(cert);
00826     rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
00827     return rv;
00828 }
00829 
00830 PRBool
00831 NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
00832 {
00833     if (!sigd) {
00834         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00835         return PR_FALSE;
00836     }
00837     if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
00838        return PR_TRUE;
00839     else if (sigd->crls != NULL && sigd->crls[0] != NULL)
00840        return PR_TRUE;
00841     else
00842        return PR_FALSE;
00843 }
00844 
00845 SECStatus
00846 NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
00847                             NSSCMSSignerInfo *signerinfo)
00848 {
00849     void *mark;
00850     SECStatus rv;
00851     SECOidTag digestalgtag;
00852     PLArenaPool *poolp;
00853 
00854     if (!sigd || !signerinfo) {
00855         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00856         return SECFailure;
00857     }
00858 
00859     poolp = sigd->cmsg->poolp;
00860 
00861     mark = PORT_ArenaMark(poolp);
00862 
00863     /* add signerinfo */
00864     rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
00865     if (rv != SECSuccess)
00866        goto loser;
00867 
00868     /*
00869      * add empty digest
00870      * Empty because we don't have it yet. Either it gets created during encoding
00871      * (if the data is present) or has to be set externally.
00872      * XXX maybe pass it in optionally?
00873      */
00874     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
00875     rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
00876     if (rv != SECSuccess)
00877        goto loser;
00878 
00879     /*
00880      * The last thing to get consistency would be adding the digest.
00881      */
00882 
00883     PORT_ArenaUnmark(poolp, mark);
00884     return SECSuccess;
00885 
00886 loser:
00887     PORT_ArenaRelease (poolp, mark);
00888     return SECFailure;
00889 }
00890 
00891 /*
00892  * NSS_CMSSignedData_SetDigests - set a signedData's digests member
00893  *
00894  * "digestalgs" - array of digest algorithm IDs
00895  * "digests"    - array of digests corresponding to the digest algorithms
00896  */
00897 SECStatus
00898 NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
00899                             SECAlgorithmID **digestalgs,
00900                             SECItem **digests)
00901 {
00902     int cnt, i, idx;
00903 
00904     if (!sigd || !digestalgs || !digests) {
00905         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00906         return SECFailure;
00907     }
00908 
00909     if (sigd->digestAlgorithms == NULL) {
00910        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00911        return SECFailure;
00912     }
00913 
00914     /* we assume that the digests array is just not there yet */
00915     PORT_Assert(sigd->digests == NULL);
00916     if (sigd->digests != NULL) {
00917        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
00918        return SECFailure;
00919     }
00920 
00921     /* now allocate one (same size as digestAlgorithms) */
00922     cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
00923     sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
00924     if (sigd->digests == NULL) {
00925        PORT_SetError(SEC_ERROR_NO_MEMORY);
00926        return SECFailure;
00927     }
00928 
00929     for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
00930        /* try to find the sigd's i'th digest algorithm in the array we passed in */
00931        idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
00932        if (idx < 0) {
00933            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
00934            return SECFailure;
00935        }
00936        if (!digests[idx]) {
00937            /* We have no digest for this algorithm, probably because it is 
00938            ** unrecognized or unsupported.  We'll ignore this here.  If this 
00939            ** digest is needed later, an error will be be generated then.
00940            */
00941            continue;
00942        }
00943 
00944        /* found it - now set it */
00945        if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
00946            SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
00947        {
00948            PORT_SetError(SEC_ERROR_NO_MEMORY);
00949            return SECFailure;
00950        }
00951     }
00952     return SECSuccess;
00953 }
00954 
00955 SECStatus
00956 NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
00957                             SECOidTag digestalgtag,
00958                             SECItem *digestdata)
00959 {
00960     SECItem *digest = NULL;
00961     PLArenaPool *poolp;
00962     void *mark;
00963     int n, cnt;
00964 
00965     if (!sigd) {
00966         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00967         return SECFailure;
00968     }
00969 
00970     poolp = sigd->cmsg->poolp;
00971 
00972     mark = PORT_ArenaMark(poolp);
00973 
00974    
00975     if (digestdata) {
00976         digest = (SECItem *) PORT_ArenaZAlloc(poolp,sizeof(SECItem));
00977 
00978        /* copy digestdata item to arena (in case we have it and are not only making room) */
00979        if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
00980            goto loser;
00981     }
00982 
00983     /* now allocate one (same size as digestAlgorithms) */
00984     if (sigd->digests == NULL) {
00985         cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
00986         sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
00987         if (sigd->digests == NULL) {
00988                PORT_SetError(SEC_ERROR_NO_MEMORY);
00989                return SECFailure;
00990         }
00991     }
00992 
00993     n = -1;
00994     if (sigd->digestAlgorithms != NULL)
00995        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
00996 
00997     /* if not found, add a digest */
00998     if (n < 0) {
00999        if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
01000            goto loser;
01001     } else {
01002        /* replace NULL pointer with digest item (and leak previous value) */
01003        sigd->digests[n] = digest;
01004     }
01005 
01006     PORT_ArenaUnmark(poolp, mark);
01007     return SECSuccess;
01008 
01009 loser:
01010     PORT_ArenaRelease(poolp, mark);
01011     return SECFailure;
01012 }
01013 
01014 SECStatus
01015 NSS_CMSSignedData_AddDigest(PRArenaPool *poolp,
01016                             NSSCMSSignedData *sigd,
01017                             SECOidTag digestalgtag,
01018                             SECItem *digest)
01019 {
01020     SECAlgorithmID *digestalg;
01021     void *mark;
01022 
01023     if (!sigd || !poolp) {
01024         PORT_SetError(SEC_ERROR_INVALID_ARGS);
01025         return SECFailure;
01026     }
01027 
01028     mark = PORT_ArenaMark(poolp);
01029 
01030     digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
01031     if (digestalg == NULL)
01032        goto loser;
01033 
01034     if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
01035        goto loser;
01036 
01037     if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
01038        /* even if digest is NULL, add dummy to have same-size array */
01039        NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
01040     {
01041        goto loser;
01042     }
01043 
01044     PORT_ArenaUnmark(poolp, mark);
01045     return SECSuccess;
01046 
01047 loser:
01048     PORT_ArenaRelease(poolp, mark);
01049     return SECFailure;
01050 }
01051 
01052 /* XXX This function doesn't set the error code on failure. */
01053 SECItem *
01054 NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
01055 {
01056     int n;
01057 
01058     if (!sigd) {
01059         PORT_SetError(SEC_ERROR_INVALID_ARGS);
01060         return NULL;
01061     }
01062 
01063     if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) {
01064         PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
01065        return NULL;
01066     }
01067 
01068     n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
01069 
01070     return (n < 0) ? NULL : sigd->digests[n];
01071 }
01072 
01073 /* =============================================================================
01074  * Misc. utility functions
01075  */
01076 
01077 /*
01078  * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
01079  *
01080  * cert          - base certificates that will be included
01081  * include_chain - if true, include the complete cert chain for cert
01082  *
01083  * More certs and chains can be added via AddCertificate and AddCertChain.
01084  *
01085  * An error results in a return value of NULL and an error set.
01086  *
01087  * XXXX CRLs
01088  */
01089 NSSCMSSignedData *
01090 NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
01091 {
01092     NSSCMSSignedData *sigd;
01093     void *mark;
01094     PLArenaPool *poolp;
01095     SECStatus rv;
01096 
01097     if (!cmsg || !cert) {
01098         PORT_SetError(SEC_ERROR_INVALID_ARGS);
01099         return NULL;
01100     }
01101 
01102     poolp = cmsg->poolp;
01103     mark = PORT_ArenaMark(poolp);
01104 
01105     sigd = NSS_CMSSignedData_Create(cmsg);
01106     if (sigd == NULL)
01107        goto loser;
01108 
01109     /* no signerinfos, thus no digestAlgorithms */
01110 
01111     /* but certs */
01112     if (include_chain) {
01113        rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
01114     } else {
01115        rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
01116     }
01117     if (rv != SECSuccess)
01118        goto loser;
01119 
01120     /* RFC2630 5.2 sez:
01121      * In the degenerate case where there are no signers, the
01122      * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
01123      * case, the content type within the EncapsulatedContentInfo value being
01124      * "signed" should be id-data (as defined in section 4), and the content
01125      * field of the EncapsulatedContentInfo value should be omitted.
01126      */
01127     rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
01128     if (rv != SECSuccess)
01129        goto loser;
01130 
01131     PORT_ArenaUnmark(poolp, mark);
01132     return sigd;
01133 
01134 loser:
01135     if (sigd)
01136        NSS_CMSSignedData_Destroy(sigd);
01137     PORT_ArenaRelease(poolp, mark);
01138     return NULL;
01139 }
01140 
01141 /* TODO:
01142  * NSS_CMSSignerInfo_GetReceiptRequest()
01143  * NSS_CMSSignedData_HasReceiptRequest()
01144  * easy way to iterate over signers
01145  */
01146