michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * CMS signedData methods. michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "cert.h" michael@0: /*#include "cdbhdl.h"*/ michael@0: #include "secasn1.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: michael@0: NSSCMSSignedData * michael@0: NSS_CMSSignedData_Create(NSSCMSMessage *cmsg) michael@0: { michael@0: void *mark; michael@0: NSSCMSSignedData *sigd; michael@0: PLArenaPool *poolp; michael@0: michael@0: if (!cmsg) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: michael@0: poolp = cmsg->poolp; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSSignedData)); michael@0: if (sigd == NULL) michael@0: goto loser; michael@0: michael@0: sigd->cmsg = cmsg; michael@0: michael@0: /* signerInfos, certs, certlists, crls are all empty */ michael@0: /* version is set in NSS_CMSSignedData_Finalize() */ michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return sigd; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(poolp, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: void michael@0: NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd) michael@0: { michael@0: CERTCertificate **certs, **tempCerts, *cert; michael@0: CERTCertificateList **certlists, *certlist; michael@0: NSSCMSSignerInfo **signerinfos, *si; michael@0: michael@0: if (sigd == NULL) michael@0: return; michael@0: michael@0: certs = sigd->certs; michael@0: tempCerts = sigd->tempCerts; michael@0: certlists = sigd->certLists; michael@0: signerinfos = sigd->signerInfos; michael@0: michael@0: if (certs != NULL) { michael@0: while ((cert = *certs++) != NULL) michael@0: CERT_DestroyCertificate (cert); michael@0: } michael@0: michael@0: if (tempCerts != NULL) { michael@0: while ((cert = *tempCerts++) != NULL) michael@0: CERT_DestroyCertificate (cert); michael@0: } michael@0: michael@0: if (certlists != NULL) { michael@0: while ((certlist = *certlists++) != NULL) michael@0: CERT_DestroyCertificateList (certlist); michael@0: } michael@0: michael@0: if (signerinfos != NULL) { michael@0: while ((si = *signerinfos++) != NULL) michael@0: NSS_CMSSignerInfo_Destroy(si); michael@0: } michael@0: michael@0: /* everything's in a pool, so don't worry about the storage */ michael@0: NSS_CMSContentInfo_Destroy(&(sigd->contentInfo)); michael@0: michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData michael@0: * before start of encoding. michael@0: * michael@0: * In detail: michael@0: * - find out about the right value to put into sigd->version michael@0: * - come up with a list of digestAlgorithms (which should be the union of the algorithms michael@0: * in the signerinfos). michael@0: * If we happen to have a pre-set list of algorithms (and digest values!), we michael@0: * check if we have all the signerinfos' algorithms. If not, this is an error. michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd) michael@0: { michael@0: NSSCMSSignerInfo *signerinfo; michael@0: SECOidTag digestalgtag; michael@0: SECItem *dummy; michael@0: int version; michael@0: SECStatus rv; michael@0: PRBool haveDigests = PR_FALSE; michael@0: int n, i; michael@0: PLArenaPool *poolp; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: poolp = sigd->cmsg->poolp; michael@0: michael@0: /* we assume that we have precomputed digests if there is a list of algorithms, and */ michael@0: /* a chunk of data for each of those algorithms */ michael@0: if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) { michael@0: for (i=0; sigd->digestAlgorithms[i] != NULL; i++) { michael@0: if (sigd->digests[i] == NULL) michael@0: break; michael@0: } michael@0: if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */ michael@0: haveDigests = PR_TRUE; /* yes: we must have all the digests */ michael@0: } michael@0: michael@0: version = NSS_CMS_SIGNED_DATA_VERSION_BASIC; michael@0: michael@0: /* RFC2630 5.1 "version is the syntax version number..." */ michael@0: if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA) michael@0: version = NSS_CMS_SIGNED_DATA_VERSION_EXT; michael@0: michael@0: /* prepare all the SignerInfos (there may be none) */ michael@0: for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) { michael@0: signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i); michael@0: michael@0: /* RFC2630 5.1 "version is the syntax version number..." */ michael@0: if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN) michael@0: version = NSS_CMS_SIGNED_DATA_VERSION_EXT; michael@0: michael@0: /* collect digestAlgorithms from SignerInfos */ michael@0: /* (we need to know which algorithms we have when the content comes in) */ michael@0: /* do not overwrite any existing digestAlgorithms (and digest) */ michael@0: digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); michael@0: n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); michael@0: if (n < 0 && haveDigests) { michael@0: /* oops, there is a digestalg we do not have a digest for */ michael@0: /* but we were supposed to have all the digests already... */ michael@0: goto loser; michael@0: } else if (n < 0) { michael@0: /* add the digestAlgorithm & a NULL digest */ michael@0: rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: } else { michael@0: /* found it, nothing to do */ michael@0: } michael@0: } michael@0: michael@0: dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version); michael@0: if (dummy == NULL) michael@0: return SECFailure; michael@0: michael@0: /* this is a SET OF, so we need to sort them guys */ michael@0: rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms, michael@0: SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), michael@0: (void **)sigd->digests); michael@0: if (rv != SECSuccess) michael@0: return SECFailure; michael@0: michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd) michael@0: { michael@0: SECStatus rv; michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: /* set up the digests */ michael@0: if (sigd->digests && sigd->digests[0]) { michael@0: sigd->contentInfo.privateInfo->digcx = NULL; /* don't attempt to make new ones. */ michael@0: } else if (sigd->digestAlgorithms != NULL) { michael@0: sigd->contentInfo.privateInfo->digcx = michael@0: NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms); michael@0: if (sigd->contentInfo.privateInfo->digcx == NULL) michael@0: return SECFailure; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData michael@0: * after all the encapsulated data was passed through the encoder. michael@0: * michael@0: * In detail: michael@0: * - create the signatures in all the SignerInfos michael@0: * michael@0: * Please note that nothing is done to the Certificates and CRLs in the message - this michael@0: * is entirely the responsibility of our callers. michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd) michael@0: { michael@0: NSSCMSSignerInfo **signerinfos, *signerinfo; michael@0: NSSCMSContentInfo *cinfo; michael@0: SECOidTag digestalgtag; michael@0: SECStatus ret = SECFailure; michael@0: SECStatus rv; michael@0: SECItem *contentType; michael@0: int certcount; michael@0: int i, ci, cli, n, rci, si; michael@0: PLArenaPool *poolp; michael@0: CERTCertificateList *certlist; michael@0: extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[]; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: poolp = sigd->cmsg->poolp; michael@0: cinfo = &(sigd->contentInfo); michael@0: michael@0: /* did we have digest calculation going on? */ michael@0: if (cinfo->privateInfo && cinfo->privateInfo->digcx) { michael@0: rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp, michael@0: &(sigd->digests)); michael@0: /* error has been set by NSS_CMSDigestContext_FinishMultiple */ michael@0: cinfo->privateInfo->digcx = NULL; michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: } michael@0: michael@0: signerinfos = sigd->signerInfos; michael@0: certcount = 0; michael@0: michael@0: /* prepare all the SignerInfos (there may be none) */ michael@0: for (i=0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) { michael@0: signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i); michael@0: michael@0: /* find correct digest for this signerinfo */ michael@0: digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); michael@0: n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); michael@0: if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) { michael@0: /* oops - digest not found */ michael@0: PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); michael@0: goto loser; michael@0: } michael@0: michael@0: /* XXX if our content is anything else but data, we need to force the michael@0: * presence of signed attributes (RFC2630 5.3 "signedAttributes is a michael@0: * collection...") */ michael@0: michael@0: /* pass contentType here as we want a contentType attribute */ michael@0: if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL) michael@0: goto loser; michael@0: michael@0: /* sign the thing */ michael@0: rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* while we're at it, count number of certs in certLists */ michael@0: certlist = NSS_CMSSignerInfo_GetCertList(signerinfo); michael@0: if (certlist) michael@0: certcount += certlist->len; michael@0: } michael@0: michael@0: /* this is a SET OF, so we need to sort them guys */ michael@0: rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* michael@0: * now prepare certs & crls michael@0: */ michael@0: michael@0: /* count the rest of the certs */ michael@0: if (sigd->certs != NULL) { michael@0: for (ci = 0; sigd->certs[ci] != NULL; ci++) michael@0: certcount++; michael@0: } michael@0: michael@0: if (sigd->certLists != NULL) { michael@0: for (cli = 0; sigd->certLists[cli] != NULL; cli++) michael@0: certcount += sigd->certLists[cli]->len; michael@0: } michael@0: michael@0: if (certcount == 0) { michael@0: sigd->rawCerts = NULL; michael@0: } else { michael@0: /* michael@0: * Combine all of the certs and cert chains into rawcerts. michael@0: * Note: certcount is an upper bound; we may not need that many slots michael@0: * but we will allocate anyway to avoid having to do another pass. michael@0: * (The temporary space saving is not worth it.) michael@0: * michael@0: * XXX ARGH - this NEEDS to be fixed. need to come up with a decent michael@0: * SetOfDERcertficates implementation michael@0: */ michael@0: sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *)); michael@0: if (sigd->rawCerts == NULL) michael@0: return SECFailure; michael@0: michael@0: /* michael@0: * XXX Want to check for duplicates and not add *any* cert that is michael@0: * already in the set. This will be more important when we start michael@0: * dealing with larger sets of certs, dual-key certs (signing and michael@0: * encryption), etc. For the time being we can slide by... michael@0: * michael@0: * XXX ARGH - this NEEDS to be fixed. need to come up with a decent michael@0: * SetOfDERcertficates implementation michael@0: */ michael@0: rci = 0; michael@0: if (signerinfos != NULL) { michael@0: for (si = 0; signerinfos[si] != NULL; si++) { michael@0: signerinfo = signerinfos[si]; michael@0: for (ci = 0; ci < signerinfo->certList->len; ci++) michael@0: sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]); michael@0: } michael@0: } michael@0: michael@0: if (sigd->certs != NULL) { michael@0: for (ci = 0; sigd->certs[ci] != NULL; ci++) michael@0: sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert); michael@0: } michael@0: michael@0: if (sigd->certLists != NULL) { michael@0: for (cli = 0; sigd->certLists[cli] != NULL; cli++) { michael@0: for (ci = 0; ci < sigd->certLists[cli]->len; ci++) michael@0: sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]); michael@0: } michael@0: } michael@0: michael@0: sigd->rawCerts[rci] = NULL; michael@0: michael@0: /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ michael@0: NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL); michael@0: } michael@0: michael@0: ret = SECSuccess; michael@0: michael@0: loser: michael@0: return ret; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd) michael@0: { michael@0: SECStatus rv; michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: /* handle issue with Windows 2003 servers and kerberos */ michael@0: if (sigd->digestAlgorithms != NULL) { michael@0: int i; michael@0: for (i=0; sigd->digestAlgorithms[i] != NULL; i++) { michael@0: SECAlgorithmID *algid = sigd->digestAlgorithms[i]; michael@0: SECOidTag senttag= SECOID_FindOIDTag(&algid->algorithm); michael@0: SECOidTag maptag = NSS_CMSUtil_MapSignAlgs(senttag); michael@0: michael@0: if (maptag != senttag) { michael@0: SECOidData *hashoid = SECOID_FindOIDByTag(maptag); michael@0: rv = SECITEM_CopyItem(sigd->cmsg->poolp, &algid->algorithm michael@0: ,&hashoid->oid); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* set up the digests */ michael@0: if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) { michael@0: /* if digests are already there, do nothing */ michael@0: sigd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms); michael@0: if (sigd->contentInfo.privateInfo->digcx == NULL) michael@0: return SECFailure; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a michael@0: * SignedData after all the encapsulated data was passed through the decoder. michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* did we have digest calculation going on? */ michael@0: if (sigd->contentInfo.privateInfo && sigd->contentInfo.privateInfo->digcx) { michael@0: rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.privateInfo->digcx, michael@0: sigd->cmsg->poolp, &(sigd->digests)); michael@0: /* error set by NSS_CMSDigestContext_FinishMultiple */ michael@0: sigd->contentInfo.privateInfo->digcx = NULL; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData michael@0: * after all decoding is finished. michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd) michael@0: { michael@0: NSSCMSSignerInfo **signerinfos = NULL; michael@0: int i; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* set cmsg for all the signerinfos */ michael@0: signerinfos = sigd->signerInfos; michael@0: michael@0: /* set cmsg for all the signerinfos */ michael@0: if (signerinfos) { michael@0: for (i = 0; signerinfos[i] != NULL; i++) michael@0: signerinfos[i]->cmsg = sigd->cmsg; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list michael@0: */ michael@0: NSSCMSSignerInfo ** michael@0: NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: return sigd->signerInfos; michael@0: } michael@0: michael@0: int michael@0: NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return 0; michael@0: } michael@0: return NSS_CMSArray_Count((void **)sigd->signerInfos); michael@0: } michael@0: michael@0: NSSCMSSignerInfo * michael@0: NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: return sigd->signerInfos[i]; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list michael@0: */ michael@0: SECAlgorithmID ** michael@0: NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: return sigd->digestAlgorithms; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo michael@0: */ michael@0: NSSCMSContentInfo * michael@0: NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: return &(sigd->contentInfo); michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list michael@0: */ michael@0: SECItem ** michael@0: NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: return sigd->rawCerts; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, michael@0: SECCertUsage certusage, PRBool keepcerts) michael@0: { michael@0: int certcount; michael@0: CERTCertificate **certArray = NULL; michael@0: CERTCertList *certList = NULL; michael@0: CERTCertListNode *node; michael@0: SECStatus rv; michael@0: SECItem **rawArray; michael@0: int i; michael@0: PRTime now; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: certcount = NSS_CMSArray_Count((void **)sigd->rawCerts); michael@0: michael@0: /* get the certs in the temp DB */ michael@0: rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, michael@0: &certArray, PR_FALSE, PR_FALSE, NULL); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* save the certs so they don't get destroyed */ michael@0: for (i=0; i < certcount; i++) { michael@0: CERTCertificate *cert = certArray[i]; michael@0: if (cert) michael@0: NSS_CMSSignedData_AddTempCertificate(sigd, cert); michael@0: } michael@0: michael@0: if (!keepcerts) { michael@0: goto done; michael@0: } michael@0: michael@0: /* build a CertList for filtering */ michael@0: certList = CERT_NewCertList(); michael@0: if (certList == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: for (i=0; i < certcount; i++) { michael@0: CERTCertificate *cert = certArray[i]; michael@0: if (cert) michael@0: cert = CERT_DupCertificate(cert); michael@0: if (cert) michael@0: CERT_AddCertToListTail(certList,cert); michael@0: } michael@0: michael@0: /* filter out the certs we don't want */ michael@0: rv = CERT_FilterCertListByUsage(certList,certusage, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* go down the remaining list of certs and verify that they have michael@0: * valid chains, then import them. michael@0: */ michael@0: now = PR_Now(); michael@0: for (node = CERT_LIST_HEAD(certList) ; !CERT_LIST_END(node,certList); michael@0: node= CERT_LIST_NEXT(node)) { michael@0: CERTCertificateList *certChain; michael@0: michael@0: if (CERT_VerifyCert(certdb, node->cert, michael@0: PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) { michael@0: continue; michael@0: } michael@0: michael@0: certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); michael@0: if (!certChain) { michael@0: continue; michael@0: } michael@0: michael@0: /* michael@0: * CertChain returns an array of SECItems, import expects an array of michael@0: * SECItem pointers. Create the SECItem Pointers from the array of michael@0: * SECItems. michael@0: */ michael@0: rawArray = (SECItem **)PORT_Alloc(certChain->len*sizeof (SECItem *)); michael@0: if (!rawArray) { michael@0: CERT_DestroyCertificateList(certChain); michael@0: continue; michael@0: } michael@0: for (i=0; i < certChain->len; i++) { michael@0: rawArray[i] = &certChain->certs[i]; michael@0: } michael@0: (void )CERT_ImportCerts(certdb, certusage, certChain->len, michael@0: rawArray, NULL, keepcerts, PR_FALSE, NULL); michael@0: PORT_Free(rawArray); michael@0: CERT_DestroyCertificateList(certChain); michael@0: } michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: /* XXX CRL handling */ michael@0: michael@0: done: michael@0: if (sigd->signerInfos != NULL) { michael@0: /* fill in all signerinfo's certs */ michael@0: for (i = 0; sigd->signerInfos[i] != NULL; i++) michael@0: (void)NSS_CMSSignerInfo_GetSigningCertificate( michael@0: sigd->signerInfos[i], certdb); michael@0: } michael@0: michael@0: loser: michael@0: /* now free everything */ michael@0: if (certArray) { michael@0: CERT_DestroyCertArray(certArray,certcount); michael@0: } michael@0: if (certList) { michael@0: CERT_DestroyCertList(certList); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * XXX the digests need to be passed in BETWEEN the decoding and the verification in case michael@0: * of external signatures! michael@0: */ michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_VerifySignerInfo - check the signatures. michael@0: * michael@0: * The digests were either calculated during decoding (and are stored in the michael@0: * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests. michael@0: * michael@0: * The verification checks if the signing cert is valid and has a trusted chain michael@0: * for the purpose specified by "certusage". michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i, michael@0: CERTCertDBHandle *certdb, SECCertUsage certusage) michael@0: { michael@0: NSSCMSSignerInfo *signerinfo; michael@0: NSSCMSContentInfo *cinfo; michael@0: SECOidData *algiddata; michael@0: SECItem *contentType, *digest; michael@0: SECOidTag oidTag; michael@0: SECStatus rv; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: cinfo = &(sigd->contentInfo); michael@0: michael@0: signerinfo = sigd->signerInfos[i]; michael@0: michael@0: /* verify certificate */ michael@0: rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage); michael@0: if (rv != SECSuccess) michael@0: return rv; /* error is set */ michael@0: michael@0: /* find digest and contentType for signerinfo */ michael@0: algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo); michael@0: oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN; michael@0: digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag); michael@0: /* NULL digest is acceptable. */ michael@0: contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo); michael@0: /* NULL contentType is acceptable. */ michael@0: michael@0: /* now verify signature */ michael@0: rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd, michael@0: CERTCertDBHandle *certdb, michael@0: SECCertUsage usage) michael@0: { michael@0: CERTCertificate *cert; michael@0: SECStatus rv = SECSuccess; michael@0: int i; michael@0: int count; michael@0: PRTime now; michael@0: michael@0: if (!sigd || !certdb || !sigd->rawCerts) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: count = NSS_CMSArray_Count((void**)sigd->rawCerts); michael@0: now = PR_Now(); michael@0: for (i=0; i < count; i++) { michael@0: if (sigd->certs && sigd->certs[i]) { michael@0: cert = CERT_DupCertificate(sigd->certs[i]); michael@0: } else { michael@0: cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]); michael@0: if (!cert) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: } michael@0: rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now, michael@0: NULL, NULL); michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_HasDigests - see if we have digests in place michael@0: */ michael@0: PRBool michael@0: NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return PR_FALSE; michael@0: } michael@0: return (sigd->digests != NULL); michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: if (!sigd || !certlist) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */ michael@0: rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert) michael@0: { michael@0: CERTCertificateList *certlist; michael@0: SECCertUsage usage; michael@0: SECStatus rv; michael@0: michael@0: usage = certUsageEmailSigner; michael@0: michael@0: if (!sigd || !cert) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* do not include root */ michael@0: certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE); michael@0: if (certlist == NULL) michael@0: return SECFailure; michael@0: michael@0: rv = NSS_CMSSignedData_AddCertList(sigd, certlist); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: extern SECStatus michael@0: NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert) michael@0: { michael@0: CERTCertificate *c; michael@0: SECStatus rv; michael@0: michael@0: if (!sigd || !cert) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: c = CERT_DupCertificate(cert); michael@0: rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert) michael@0: { michael@0: CERTCertificate *c; michael@0: SECStatus rv; michael@0: michael@0: if (!sigd || !cert) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: c = CERT_DupCertificate(cert); michael@0: rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c); michael@0: return rv; michael@0: } michael@0: michael@0: PRBool michael@0: NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd) michael@0: { michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return PR_FALSE; michael@0: } michael@0: if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL) michael@0: return PR_TRUE; michael@0: else if (sigd->crls != NULL && sigd->crls[0] != NULL) michael@0: return PR_TRUE; michael@0: else michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd, michael@0: NSSCMSSignerInfo *signerinfo) michael@0: { michael@0: void *mark; michael@0: SECStatus rv; michael@0: SECOidTag digestalgtag; michael@0: PLArenaPool *poolp; michael@0: michael@0: if (!sigd || !signerinfo) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: poolp = sigd->cmsg->poolp; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: /* add signerinfo */ michael@0: rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* michael@0: * add empty digest michael@0: * Empty because we don't have it yet. Either it gets created during encoding michael@0: * (if the data is present) or has to be set externally. michael@0: * XXX maybe pass it in optionally? michael@0: */ michael@0: digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); michael@0: rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* michael@0: * The last thing to get consistency would be adding the digest. michael@0: */ michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease (poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_SetDigests - set a signedData's digests member michael@0: * michael@0: * "digestalgs" - array of digest algorithm IDs michael@0: * "digests" - array of digests corresponding to the digest algorithms michael@0: */ michael@0: SECStatus michael@0: NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd, michael@0: SECAlgorithmID **digestalgs, michael@0: SECItem **digests) michael@0: { michael@0: int cnt, i, idx; michael@0: michael@0: if (!sigd || !digestalgs || !digests) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (sigd->digestAlgorithms == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* we assume that the digests array is just not there yet */ michael@0: PORT_Assert(sigd->digests == NULL); michael@0: if (sigd->digests != NULL) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* now allocate one (same size as digestAlgorithms) */ michael@0: cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms); michael@0: sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *)); michael@0: if (sigd->digests == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) { michael@0: /* try to find the sigd's i'th digest algorithm in the array we passed in */ michael@0: idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]); michael@0: if (idx < 0) { michael@0: PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); michael@0: return SECFailure; michael@0: } michael@0: if (!digests[idx]) { michael@0: /* We have no digest for this algorithm, probably because it is michael@0: ** unrecognized or unsupported. We'll ignore this here. If this michael@0: ** digest is needed later, an error will be be generated then. michael@0: */ michael@0: continue; michael@0: } michael@0: michael@0: /* found it - now set it */ michael@0: if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL || michael@0: SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) michael@0: { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd, michael@0: SECOidTag digestalgtag, michael@0: SECItem *digestdata) michael@0: { michael@0: SECItem *digest = NULL; michael@0: PLArenaPool *poolp; michael@0: void *mark; michael@0: int n, cnt; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: poolp = sigd->cmsg->poolp; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: michael@0: if (digestdata) { michael@0: digest = (SECItem *) PORT_ArenaZAlloc(poolp,sizeof(SECItem)); michael@0: michael@0: /* copy digestdata item to arena (in case we have it and are not only making room) */ michael@0: if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess) michael@0: goto loser; michael@0: } michael@0: michael@0: /* now allocate one (same size as digestAlgorithms) */ michael@0: if (sigd->digests == NULL) { michael@0: cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms); michael@0: sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *)); michael@0: if (sigd->digests == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: n = -1; michael@0: if (sigd->digestAlgorithms != NULL) michael@0: n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); michael@0: michael@0: /* if not found, add a digest */ michael@0: if (n < 0) { michael@0: if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess) michael@0: goto loser; michael@0: } else { michael@0: /* replace NULL pointer with digest item (and leak previous value) */ michael@0: sigd->digests[n] = digest; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSSignedData_AddDigest(PLArenaPool *poolp, michael@0: NSSCMSSignedData *sigd, michael@0: SECOidTag digestalgtag, michael@0: SECItem *digest) michael@0: { michael@0: SECAlgorithmID *digestalg; michael@0: void *mark; michael@0: michael@0: if (!sigd || !poolp) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID)); michael@0: if (digestalg == NULL) michael@0: goto loser; michael@0: michael@0: if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */ michael@0: goto loser; michael@0: michael@0: if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess || michael@0: /* even if digest is NULL, add dummy to have same-size array */ michael@0: NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess) michael@0: { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* XXX This function doesn't set the error code on failure. */ michael@0: SECItem * michael@0: NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag) michael@0: { michael@0: int n; michael@0: michael@0: if (!sigd) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: michael@0: if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) { michael@0: PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); michael@0: return NULL; michael@0: } michael@0: michael@0: n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); michael@0: michael@0: return (n < 0) ? NULL : sigd->digests[n]; michael@0: } michael@0: michael@0: /* ============================================================================= michael@0: * Misc. utility functions michael@0: */ michael@0: michael@0: /* michael@0: * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData. michael@0: * michael@0: * cert - base certificates that will be included michael@0: * include_chain - if true, include the complete cert chain for cert michael@0: * michael@0: * More certs and chains can be added via AddCertificate and AddCertChain. michael@0: * michael@0: * An error results in a return value of NULL and an error set. michael@0: * michael@0: * XXXX CRLs michael@0: */ michael@0: NSSCMSSignedData * michael@0: NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain) michael@0: { michael@0: NSSCMSSignedData *sigd; michael@0: void *mark; michael@0: PLArenaPool *poolp; michael@0: SECStatus rv; michael@0: michael@0: if (!cmsg || !cert) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return NULL; michael@0: } michael@0: michael@0: poolp = cmsg->poolp; michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: sigd = NSS_CMSSignedData_Create(cmsg); michael@0: if (sigd == NULL) michael@0: goto loser; michael@0: michael@0: /* no signerinfos, thus no digestAlgorithms */ michael@0: michael@0: /* but certs */ michael@0: if (include_chain) { michael@0: rv = NSS_CMSSignedData_AddCertChain(sigd, cert); michael@0: } else { michael@0: rv = NSS_CMSSignedData_AddCertificate(sigd, cert); michael@0: } michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* RFC2630 5.2 sez: michael@0: * In the degenerate case where there are no signers, the michael@0: * EncapsulatedContentInfo value being "signed" is irrelevant. In this michael@0: * case, the content type within the EncapsulatedContentInfo value being michael@0: * "signed" should be id-data (as defined in section 4), and the content michael@0: * field of the EncapsulatedContentInfo value should be omitted. michael@0: */ michael@0: rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return sigd; michael@0: michael@0: loser: michael@0: if (sigd) michael@0: NSS_CMSSignedData_Destroy(sigd); michael@0: PORT_ArenaRelease(poolp, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* TODO: michael@0: * NSS_CMSSignerInfo_GetReceiptRequest() michael@0: * NSS_CMSSignedData_HasReceiptRequest() michael@0: * easy way to iterate over signers michael@0: */ michael@0: