/* * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message */ SECStatus NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, SECCertUsage usage) { CERTCertificate *cert; SECStatus rv = SECSuccess; int i; int count; PRTime now; if (!sigd || !certdb || !sigd->rawCerts) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } count = NSS_CMSArray_Count((void**)sigd->rawCerts); now = PR_Now(); for (i=0; i < count; i++) { if (sigd->certs && sigd->certs[i]) { cert = CERT_DupCertificate(sigd->certs[i]); } else { cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]); if (!cert) { rv = SECFailure; break; } } rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now, NULL, NULL); CERT_DestroyCertificate(cert); } return rv; }
SECStatus NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag, SECItem *digestdata) { SECItem *digest = NULL; PLArenaPool *poolp; void *mark; int n, cnt; if (!sigd) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } poolp = sigd->cmsg->poolp; mark = PORT_ArenaMark(poolp); if (digestdata) { digest = (SECItem *) PORT_ArenaZAlloc(poolp,sizeof(SECItem)); /* copy digestdata item to arena (in case we have it and are not only making room) */ if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess) goto loser; } /* now allocate one (same size as digestAlgorithms) */ if (sigd->digests == NULL) { cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms); sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *)); if (sigd->digests == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } } n = -1; if (sigd->digestAlgorithms != NULL) n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); /* if not found, add a digest */ if (n < 0) { if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess) goto loser; } else { /* replace NULL pointer with digest item (and leak previous value) */ sigd->digests[n] = digest; } PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); return SECFailure; }
int NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd) { if (!sigd) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return 0; } return NSS_CMSArray_Count((void **)sigd->signerInfos); }
/* * NSS_CMSSignedData_SetDigests - set a signedData's digests member * * "digestalgs" - array of digest algorithm IDs * "digests" - array of digests corresponding to the digest algorithms */ SECStatus NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd, SECAlgorithmID **digestalgs, SECItem **digests) { int cnt, i, idx; if (!sigd || !digestalgs || !digests) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (sigd->digestAlgorithms == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* we assume that the digests array is just not there yet */ PORT_Assert(sigd->digests == NULL); if (sigd->digests != NULL) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } /* now allocate one (same size as digestAlgorithms) */ cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms); sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *)); if (sigd->digests == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) { /* try to find the sigd's i'th digest algorithm in the array we passed in */ idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]); if (idx < 0) { PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); return SECFailure; } if (!digests[idx]) { /* We have no digest for this algorithm, probably because it is ** unrecognized or unsupported. We'll ignore this here. If this ** digest is needed later, an error will be be generated then. */ continue; } /* found it - now set it */ if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL || SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } } return SECSuccess; }
/* * NSS_CMSArray_Sort - sort an array in place * * If "secondary" or "tertiary are not NULL, it must be arrays with the same * number of elements as "primary". The same reordering will get applied to it. * * "compare" is a function that returns * < 0 when the first element is less than the second * = 0 when the first element is equal to the second * > 0 when the first element is greater than the second * to acheive ascending ordering. */ void NSS_CMSArray_Sort(void **primary, int (*compare)(void *,void *), void **secondary, void **tertiary) { int n, i, limit, lastxchg; void *tmp; n = NSS_CMSArray_Count(primary); PORT_Assert(secondary == NULL || NSS_CMSArray_Count(secondary) == n); PORT_Assert(tertiary == NULL || NSS_CMSArray_Count(tertiary) == n); if (n <= 1) /* ordering is fine */ return; /* yes, ladies and gentlemen, it's BUBBLE SORT TIME! */ limit = n - 1; while (1) { lastxchg = 0; for (i = 0; i < limit; i++) { if ((*compare)(primary[i], primary[i+1]) > 0) { /* exchange the neighbours */ tmp = primary[i+1]; primary[i+1] = primary[i]; primary[i] = tmp; if (secondary) { /* secondary array? */ tmp = secondary[i+1]; /* exchange there as well */ secondary[i+1] = secondary[i]; secondary[i] = tmp; } if (tertiary) { /* tertiary array? */ tmp = tertiary[i+1]; /* exchange there as well */ tertiary[i+1] = tertiary[i]; tertiary[i] = tmp; } lastxchg = i+1; /* index of the last element bubbled up */ } } if (lastxchg == 0) /* no exchanges, so array is sorted */ break; /* we're done */ limit = lastxchg; /* array is sorted up to [limit] */ } }
/* * NSS_CMSArray_SortByDER - sort array of objects by objects' DER encoding * * make sure that the order of the objects guarantees valid DER (which must be * in lexigraphically ascending order for a SET OF); if reordering is necessary it * will be done in place (in objs). */ SECStatus NSS_CMSArray_SortByDER(void **objs, const SEC_ASN1Template *objtemplate, void **objs2) { PRArenaPool *poolp; int num_objs; SECItem **enc_objs; SECStatus rv = SECFailure; int i; if (objs == NULL) /* already sorted */ return SECSuccess; num_objs = NSS_CMSArray_Count((void **)objs); if (num_objs == 0 || num_objs == 1) /* already sorted. */ return SECSuccess; poolp = PORT_NewArena (1024); /* arena for temporaries */ if (poolp == NULL) return SECFailure; /* no memory; nothing we can do... */ /* * Allocate arrays to hold the individual encodings which we will use * for comparisons and the reordered attributes as they are sorted. */ enc_objs = (SECItem **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SECItem *)); if (enc_objs == NULL) goto loser; /* DER encode each individual object. */ for (i = 0; i < num_objs; i++) { enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate); if (enc_objs[i] == NULL) goto loser; } enc_objs[num_objs] = NULL; /* now compare and sort objs by the order of enc_objs */ NSS_CMSArray_Sort((void **)enc_objs, NSS_CMSUtil_DERCompare, objs, objs2); rv = SECSuccess; loser: PORT_FreeArena (poolp, PR_FALSE); return rv; }
SECStatus NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, SECCertUsage certusage, PRBool keepcerts) { int certcount; CERTCertificate **certArray = NULL; CERTCertList *certList = NULL; CERTCertListNode *node; SECStatus rv; SECItem **rawArray; int i; PRTime now; if (!sigd) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } certcount = NSS_CMSArray_Count((void **)sigd->rawCerts); /* get the certs in the temp DB */ rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, &certArray, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { goto loser; } /* save the certs so they don't get destroyed */ for (i = 0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) NSS_CMSSignedData_AddTempCertificate(sigd, cert); } if (!keepcerts) { goto done; } /* build a CertList for filtering */ certList = CERT_NewCertList(); if (certList == NULL) { rv = SECFailure; goto loser; } for (i = 0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) cert = CERT_DupCertificate(cert); if (cert) CERT_AddCertToListTail(certList, cert); } /* filter out the certs we don't want */ rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE); if (rv != SECSuccess) { goto loser; } /* go down the remaining list of certs and verify that they have * valid chains, then import them. */ now = PR_Now(); for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { CERTCertificateList *certChain; if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) { continue; } certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); if (!certChain) { continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *)); if (!rawArray) { CERT_DestroyCertificateList(certChain); continue; } for (i = 0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } (void)CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL, keepcerts, PR_FALSE, NULL); PORT_Free(rawArray); CERT_DestroyCertificateList(certChain); } rv = SECSuccess; /* XXX CRL handling */ done: if (sigd->signerInfos != NULL) { /* fill in all signerinfo's certs */ for (i = 0; sigd->signerInfos[i] != NULL; i++) (void)NSS_CMSSignerInfo_GetSigningCertificate( sigd->signerInfos[i], certdb); } loser: /* now free everything */ if (certArray) { CERT_DestroyCertArray(certArray, certcount); } if (certList) { CERT_DestroyCertList(certList); } return rv; }
/* * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, * derive bulk key & set up our contentinfo */ SECStatus NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd) { NSSCMSRecipientInfo *ri; PK11SymKey *bulkkey = NULL; SECOidTag bulkalgtag; SECAlgorithmID *bulkalg; SECStatus rv = SECFailure; NSSCMSContentInfo *cinfo; NSSCMSRecipient **recipient_list = NULL; NSSCMSRecipient *recipient; int rlIndex; if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) { PORT_SetError(SEC_ERROR_BAD_DATA); #if 0 PORT_SetErrorString("No recipient data in envelope."); #endif goto loser; } /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */ /* get the cert and private key for it right away */ recipient_list = nss_cms_recipient_list_create(envd->recipientInfos); if (recipient_list == NULL) goto loser; /* what about multiple recipientInfos that match? * especially if, for some reason, we could not produce a bulk key with the first match?! * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList... * maybe later... */ rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg); /* if that fails, then we're not an intended recipient and cannot decrypt */ if (rlIndex < 0) { PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT); #if 0 PORT_SetErrorString("Cannot decrypt data because proper key cannot be found."); #endif goto loser; } recipient = recipient_list[rlIndex]; if (!recipient->cert || !recipient->privkey) { /* XXX should set an error code ?!? */ goto loser; } /* get a pointer to "our" recipientinfo */ ri = envd->recipientInfos[recipient->riIndex]; cinfo = &(envd->contentInfo); bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); if (bulkalgtag == SEC_OID_UNKNOWN) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); } else bulkkey = NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex, recipient->cert, recipient->privkey, bulkalgtag); if (bulkkey == NULL) { /* no success finding a bulk key */ goto loser; } NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); rv = NSS_CMSContentInfo_Private_Init(cinfo); if (rv != SECSuccess) { goto loser; } rv = SECFailure; cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); if (cinfo->privateInfo->ciphcx == NULL) goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ rv = SECSuccess; loser: if (bulkkey) PK11_FreeSymKey(bulkkey); if (recipient_list != NULL) nss_cms_recipient_list_destroy(recipient_list); return rv; }