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