cmssigdata.c
1 /* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34 /* 35 * CMS signedData methods. 36 */ 37 38 #include <Security/SecCmsSignedData.h> 39 40 #include <Security/SecCmsContentInfo.h> 41 #include <Security/SecCmsDigestContext.h> 42 #include <Security/SecCmsSignerInfo.h> 43 44 #include "cmslocal.h" 45 46 #include "cert.h" 47 #include "SecAsn1Item.h" 48 #include "secoid.h" 49 50 #include <security_asn1/secasn1.h> 51 #include <security_asn1/secerr.h> 52 #include <security_asn1/secport.h> 53 54 #if !USE_CDSA_CRYPTO 55 #include <Security/SecCertificatePriv.h> 56 #endif 57 58 SecCmsSignedDataRef 59 SecCmsSignedDataCreate(SecCmsMessageRef cmsg) 60 { 61 void *mark; 62 SecCmsSignedDataRef sigd; 63 PLArenaPool *poolp; 64 65 poolp = cmsg->poolp; 66 67 mark = PORT_ArenaMark(poolp); 68 69 sigd = (SecCmsSignedDataRef)PORT_ArenaZAlloc (poolp, sizeof(SecCmsSignedData)); 70 if (sigd == NULL) 71 goto loser; 72 73 sigd->contentInfo.cmsg = cmsg; 74 75 /* signerInfos, certs, certlists, crls are all empty */ 76 /* version is set in SecCmsSignedDataFinalize() */ 77 78 PORT_ArenaUnmark(poolp, mark); 79 return sigd; 80 81 loser: 82 PORT_ArenaRelease(poolp, mark); 83 return NULL; 84 } 85 86 void 87 SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd) 88 { 89 SecCmsSignerInfoRef *signerinfos, si; 90 91 if (sigd == NULL) 92 return; 93 94 if (sigd->certs != NULL) 95 CFRelease(sigd->certs); 96 97 signerinfos = sigd->signerInfos; 98 if (signerinfos != NULL) { 99 while ((si = *signerinfos++) != NULL) 100 SecCmsSignerInfoDestroy(si); 101 } 102 103 /* everything's in a pool, so don't worry about the storage */ 104 SecCmsContentInfoDestroy(&(sigd->contentInfo)); 105 } 106 107 /* 108 * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData 109 * before start of encoding. 110 * 111 * In detail: 112 * - find out about the right value to put into sigd->version 113 * - come up with a list of digestAlgorithms (which should be the union of the algorithms 114 * in the signerinfos). 115 * If we happen to have a pre-set list of algorithms (and digest values!), we 116 * check if we have all the signerinfos' algorithms. If not, this is an error. 117 */ 118 OSStatus 119 SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd) 120 { 121 SecCmsSignerInfoRef signerinfo; 122 SECOidTag digestalgtag; 123 SecAsn1Item * dummy; 124 int version; 125 OSStatus rv; 126 Boolean haveDigests = PR_FALSE; 127 int n, i; 128 PLArenaPool *poolp; 129 130 poolp = sigd->contentInfo.cmsg->poolp; 131 132 /* we assume that we have precomputed digests if there is a list of algorithms, and */ 133 /* a chunk of data for each of those algorithms */ 134 if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) { 135 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) { 136 if (sigd->digests[i] == NULL) 137 break; 138 } 139 if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */ 140 haveDigests = PR_TRUE; /* yes: we must have all the digests */ 141 } 142 143 version = SEC_CMS_SIGNED_DATA_VERSION_BASIC; 144 145 /* RFC2630 5.1 "version is the syntax version number..." */ 146 if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA) 147 version = SEC_CMS_SIGNED_DATA_VERSION_EXT; 148 149 /* prepare all the SignerInfos (there may be none) */ 150 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { 151 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); 152 153 /* RFC2630 5.1 "version is the syntax version number..." */ 154 if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN) 155 version = SEC_CMS_SIGNED_DATA_VERSION_EXT; 156 157 /* collect digestAlgorithms from SignerInfos */ 158 /* (we need to know which algorithms we have when the content comes in) */ 159 /* do not overwrite any existing digestAlgorithms (and digest) */ 160 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 161 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 162 if (n < 0 && haveDigests) { 163 /* oops, there is a digestalg we do not have a digest for */ 164 /* but we were supposed to have all the digests already... */ 165 goto loser; 166 } else if (n < 0) { 167 /* add the digestAlgorithm & a NULL digest */ 168 rv = SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, NULL); 169 if (rv != SECSuccess) 170 goto loser; 171 } else { 172 /* found it, nothing to do */ 173 } 174 } 175 176 dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version); 177 if (dummy == NULL) 178 return SECFailure; 179 180 /* this is a SET OF, so we need to sort them guys */ 181 rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms, 182 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), 183 (void **)sigd->digests); 184 if (rv != SECSuccess) 185 return SECFailure; 186 187 return SECSuccess; 188 189 loser: 190 return SECFailure; 191 } 192 193 OSStatus 194 SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd) 195 { 196 /* set up the digests */ 197 if (sigd->digestAlgorithms != NULL) { 198 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms); 199 if (sigd->contentInfo.digcx == NULL) 200 return SECFailure; 201 } 202 return SECSuccess; 203 } 204 205 /* 206 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData 207 * after all the encapsulated data was passed through the encoder. 208 * 209 * In detail: 210 * - create the signatures in all the SignerInfos 211 * 212 * Please note that nothing is done to the Certificates and CRLs in the message - this 213 * is entirely the responsibility of our callers. 214 */ 215 OSStatus 216 SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd) 217 { 218 SecCmsSignerInfoRef *signerinfos, signerinfo; 219 SecCmsContentInfoRef cinfo; 220 SECOidTag digestalgtag; 221 OSStatus ret = SECFailure; 222 OSStatus rv; 223 SecAsn1Item * contentType; 224 CFIndex certcount; 225 int i, ci, n, rci, si; 226 PLArenaPool *poolp; 227 CFArrayRef certlist; 228 extern const SecAsn1Template SecCmsSignerInfoTemplate[]; 229 230 cinfo = &(sigd->contentInfo); 231 poolp = cinfo->cmsg->poolp; 232 233 /* did we have digest calculation going on? */ 234 if (cinfo->digcx) { 235 SecAsn1Item **digests = NULL; 236 SECAlgorithmID **digestalgs = NULL; 237 rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, &digestalgs, &digests); 238 if (rv != SECSuccess) 239 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ 240 if (digestalgs && digests) { 241 rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests); 242 if (rv != SECSuccess) 243 goto loser; /* error has been set by SecCmsSignedDataSetDigests */ 244 } 245 SecCmsDigestContextDestroy(cinfo->digcx); 246 cinfo->digcx = NULL; 247 } 248 249 signerinfos = sigd->signerInfos; 250 certcount = 0; 251 252 /* prepare all the SignerInfos (there may be none) */ 253 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { 254 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); 255 256 /* find correct digest for this signerinfo */ 257 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 258 n = SecCmsAlgArrayGetIndexByAlgTag(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 = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL) 271 goto loser; 272 273 /* sign the thing */ 274 rv = SecCmsSignerInfoSign(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 = SecCmsSignerInfoGetCertList(signerinfo); 280 if (certlist) 281 certcount += CFArrayGetCount(certlist); 282 } 283 284 /* this is a SET OF, so we need to sort them guys */ 285 rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, 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 certcount += CFArrayGetCount(sigd->certs); 296 297 if (certcount == 0) { 298 sigd->rawCerts = NULL; 299 } else { 300 /* 301 * Combine all of the certs and cert chains into rawcerts. 302 * Note: certcount is an upper bound; we may not need that many slots 303 * but we will allocate anyway to avoid having to do another pass. 304 * (The temporary space saving is not worth it.) 305 * 306 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent 307 * SetOfDERcertficates implementation 308 */ 309 sigd->rawCerts = (SecAsn1Item * *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SecAsn1Item *)); 310 if (sigd->rawCerts == NULL) 311 return SECFailure; 312 313 /* 314 * XXX Want to check for duplicates and not add *any* cert that is 315 * already in the set. This will be more important when we start 316 * dealing with larger sets of certs, dual-key certs (signing and 317 * encryption), etc. For the time being we can slide by... 318 * 319 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent 320 * SetOfDERcertficates implementation 321 */ 322 rci = 0; 323 if (signerinfos != NULL) { 324 for (si = 0; signerinfos[si] != NULL; si++) { 325 signerinfo = signerinfos[si]; 326 for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) { 327 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item)); 328 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci); 329 #if USE_CDSA_CRYPTO 330 SecCertificateGetData(cert, sigd->rawCerts[rci++]); 331 #else 332 SecAsn1Item cert_data = { SecCertificateGetLength(cert), 333 (uint8_t *)SecCertificateGetBytePtr(cert) }; 334 *(sigd->rawCerts[rci++]) = cert_data; 335 #endif 336 } 337 } 338 } 339 340 if (sigd->certs != NULL) { 341 for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) { 342 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item)); 343 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci); 344 #if USE_CDSA_CRYPTO 345 SecCertificateGetData(cert, sigd->rawCerts[rci++]); 346 #else 347 SecAsn1Item cert_data = { SecCertificateGetLength(cert), 348 (uint8_t *)SecCertificateGetBytePtr(cert) }; 349 *(sigd->rawCerts[rci++]) = cert_data; 350 #endif 351 } 352 } 353 354 sigd->rawCerts[rci] = NULL; 355 356 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ 357 SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); 358 } 359 360 ret = SECSuccess; 361 362 loser: 363 return ret; 364 } 365 366 OSStatus 367 SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd) 368 { 369 /* set up the digests, if we have digest algorithms, no digests yet, and content is attached */ 370 if (sigd->digestAlgorithms != NULL && sigd->digests == NULL /* && sigd->contentInfo.content.pointer != NULL*/) { 371 /* if digests are already there, do nothing */ 372 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms); 373 if (sigd->contentInfo.digcx == NULL) 374 return SECFailure; 375 } 376 return SECSuccess; 377 } 378 379 /* 380 * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData 381 * after all the encapsulated data was passed through the decoder. 382 */ 383 OSStatus 384 SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd) 385 { 386 OSStatus rv = SECSuccess; 387 388 /* did we have digest calculation going on? */ 389 if (sigd->contentInfo.digcx) { 390 /* @@@ we should see if data was absent vs. zero length */ 391 if (sigd->contentInfo.content.data && sigd->contentInfo.content.data->Length) { 392 SecAsn1Item * *digests = NULL; 393 SECAlgorithmID **digestalgs = NULL; 394 rv = SecCmsDigestContextFinishMultiple(sigd->contentInfo.digcx, &digestalgs, &digests); 395 if (rv != SECSuccess) 396 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ 397 rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests); 398 if (rv != SECSuccess) 399 goto loser; /* error has been set by SecCmsSignedDataSetDigests */ 400 } 401 SecCmsDigestContextDestroy(sigd->contentInfo.digcx); 402 sigd->contentInfo.digcx = NULL; 403 } 404 405 loser: 406 return rv; 407 } 408 409 /* 410 * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData 411 * after all decoding is finished. 412 */ 413 OSStatus 414 SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd) 415 { 416 SecCmsSignerInfoRef *signerinfos; 417 int i; 418 419 if (!sigd) { 420 return SECFailure; 421 } 422 423 /* set cmsg for all the signerinfos */ 424 signerinfos = sigd->signerInfos; 425 426 /* set signedData for all the signerinfos */ 427 if (signerinfos) { 428 for (i = 0; signerinfos[i] != NULL; i++) 429 signerinfos[i]->signedData = sigd; 430 } 431 432 return SECSuccess; 433 } 434 435 /* 436 * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list 437 */ 438 SecCmsSignerInfoRef * 439 SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd) 440 { 441 return sigd->signerInfos; 442 } 443 444 int 445 SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd) 446 { 447 return SecCmsArrayCount((void **)sigd->signerInfos); 448 } 449 450 SecCmsSignerInfoRef 451 SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd, int i) 452 { 453 return sigd->signerInfos[i]; 454 } 455 456 /* 457 * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list 458 */ 459 SECAlgorithmID ** 460 SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd) 461 { 462 return sigd->digestAlgorithms; 463 } 464 465 /* 466 * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo 467 */ 468 SecCmsContentInfoRef 469 SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd) 470 { 471 return &(sigd->contentInfo); 472 } 473 474 /* 475 * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list 476 */ 477 SecAsn1Item * * 478 SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd) 479 { 480 return sigd->rawCerts; 481 } 482 483 OSStatus 484 SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain, 485 SECCertUsage certusage, Boolean keepcerts) 486 { 487 OSStatus rv = -1; 488 489 #if USE_CDSA_CRYPTO 490 int ix, certcount = SecCmsArrayCount((void **)sigd->rawCerts); 491 rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL, 492 keepcerts, PR_FALSE, NULL); 493 /* XXX CRL handling */ 494 495 if (sigd->signerInfos != NULL) { 496 /* fill in all signerinfo's certs */ 497 for (ix = 0; sigd->signerInfos[ix] != NULL; i++) 498 (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[ix], keychain); 499 } 500 #else 501 // XXX we should only ever import certs for a cert only data blob 502 #endif 503 504 return rv; 505 } 506 507 /* 508 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case 509 * of external signatures! 510 */ 511 512 513 /* 514 * SecCmsSignedDataVerifySignerInfo - check the signatures. 515 * 516 * The digests were either calculated during decoding (and are stored in the 517 * signedData itself) or set after decoding using SecCmsSignedDataSetDigests. 518 * 519 * The verification checks if the signing cert is valid and has a trusted chain 520 * for the purpose specified by "policies". 521 * 522 * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly. 523 * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate(). 524 */ 525 OSStatus 526 SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i, 527 SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef) 528 { 529 SecCmsSignerInfoRef signerinfo; 530 SecCmsContentInfoRef cinfo; 531 SECOidData *algiddata; 532 SecAsn1Item *contentType, *digest; 533 OSStatus status; 534 535 if (sigd == NULL || sigd->signerInfos == NULL || i >= SecCmsSignedDataSignerInfoCount(sigd)) { 536 return errSecParam; 537 } 538 539 cinfo = &(sigd->contentInfo); 540 signerinfo = sigd->signerInfos[i]; 541 542 /* Signature or digest level verificationStatus errors should supercede 543 certificate level errors, so check the digest and signature first. */ 544 545 /* Find digest and contentType for signerinfo */ 546 algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo); 547 if (algiddata == NULL) { 548 return errSecInvalidDigestAlgorithm; 549 } 550 551 if (!sigd->digests) { 552 SECAlgorithmID **digestalgs = SecCmsSignedDataGetDigestAlgs(sigd); 553 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestalgs); 554 SecCmsSignedDataSetDigestContext(sigd, digcx); 555 SecCmsDigestContextDestroy(digcx); 556 } 557 558 digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset); 559 if (digest == NULL) { 560 return errSecDataNotAvailable; 561 } 562 563 contentType = SecCmsContentInfoGetContentTypeOID(cinfo); 564 565 /* verify signature */ 566 status = SecCmsSignerInfoVerify(signerinfo, digest, contentType); 567 #if SECTRUST_VERBOSE_DEBUG 568 syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerify returned %d, will %sverify cert", 569 (int)status, (status) ? "NOT " : ""); 570 #endif 571 if (status) { 572 return status; 573 } 574 575 /* Now verify the certificate. We only do this when the signature verification succeeds. Note that this 576 behavior is different than the macOS code. */ 577 status = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray, policies, trustRef); 578 #if SECTRUST_VERBOSE_DEBUG 579 syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerifyCertificate returned %d", (int)status); 580 #endif 581 582 return status; 583 } 584 585 #if USE_CDSA_CRYPTO 586 587 /* 588 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message 589 */ 590 OSStatus 591 SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 592 SecKeychainRef keychainOrArray, 593 CFTypeRef policies) 594 { 595 SecCertificateRef cert; 596 OSStatus rv = SECSuccess; 597 int i; 598 int count; 599 600 if (!sigd || !keychainOrArray || !sigd->rawCerts) { 601 PORT_SetError(SEC_ERROR_INVALID_ARGS); 602 return SECFailure; 603 } 604 605 count = SecCmsArrayCount((void**)sigd->rawCerts); 606 for (i=0; i < count; i++) { 607 if (sigd->certs && CFArrayGetCount(sigd->certs) > i) { 608 cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i); 609 CFRetain(cert); 610 } else { 611 cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]); 612 if (!cert) { 613 rv = SECFailure; 614 break; 615 } 616 } 617 rv |= CERT_VerifyCert(keychainOrArray, cert, policies, CFAbsoluteTimeGetCurrent(), NULL); 618 CFRelease(cert); 619 } 620 621 return rv; 622 } 623 #else 624 OSStatus 625 SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 626 SecKeychainRef keychainOrArray, 627 CFTypeRef policies) 628 { 629 OSStatus rv = SECSuccess; 630 631 if (!sigd || !keychainOrArray || !sigd->rawCerts) { 632 PORT_SetError(SEC_ERROR_INVALID_ARGS); 633 return SECFailure; 634 } 635 636 SecAsn1Item **cert_datas = sigd->rawCerts; 637 SecAsn1Item *cert_data; 638 while ((cert_data = *cert_datas++) != NULL) { 639 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length); 640 if (cert) { 641 CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **)&cert, 1, NULL); 642 rv |= CERT_VerifyCert(keychainOrArray, certs, policies, CFAbsoluteTimeGetCurrent(), NULL); 643 CFRelease(certs); 644 CFRelease(cert); 645 } 646 else 647 rv |= SECFailure; 648 } 649 650 return rv; 651 } 652 #endif 653 654 /* 655 * SecCmsSignedDataHasDigests - see if we have digests in place 656 */ 657 Boolean 658 SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd) 659 { 660 return (sigd->digests != NULL); 661 } 662 663 OSStatus 664 SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd, CFArrayRef certlist) 665 { 666 PORT_Assert(certlist != NULL); 667 668 if (certlist == NULL) 669 return SECFailure; 670 671 if (!sigd->certs) 672 sigd->certs = CFArrayCreateMutableCopy(NULL, 0, certlist); 673 else 674 { 675 CFRange certlistRange = { 0, CFArrayGetCount(certlist) }; 676 CFArrayAppendArray(sigd->certs, certlist, certlistRange); 677 } 678 679 return SECSuccess; 680 } 681 682 /* 683 * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs 684 */ 685 OSStatus 686 SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd, SecCertificateRef cert) 687 { 688 CFArrayRef certlist; 689 SECCertUsage usage; 690 OSStatus rv; 691 692 usage = certUsageEmailSigner; 693 694 /* do not include root */ 695 certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE, PR_FALSE); 696 if (certlist == NULL) 697 return SECFailure; 698 699 rv = SecCmsSignedDataAddCertList(sigd, certlist); 700 CFRelease(certlist); 701 702 return rv; 703 } 704 705 OSStatus 706 SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd, SecCertificateRef cert) 707 { 708 PORT_Assert(cert != NULL); 709 710 if (cert == NULL) 711 return SECFailure; 712 713 if (!sigd->certs) 714 sigd->certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 715 716 CFArrayAppendValue(sigd->certs, cert); 717 718 return SECSuccess; 719 } 720 721 Boolean 722 SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd) 723 { 724 if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL) 725 return PR_TRUE; 726 else if (sigd->rawCrls != NULL && sigd->rawCrls[0] != NULL) 727 return PR_TRUE; 728 else 729 return PR_FALSE; 730 } 731 732 OSStatus 733 SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd, 734 SecCmsSignerInfoRef signerinfo) 735 { 736 void *mark; 737 OSStatus rv; 738 SECOidTag digestalgtag; 739 PLArenaPool *poolp; 740 741 poolp = sigd->contentInfo.cmsg->poolp; 742 743 mark = PORT_ArenaMark(poolp); 744 745 /* add signerinfo */ 746 rv = SecCmsArrayAdd(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo); 747 if (rv != SECSuccess) 748 goto loser; 749 750 /* 751 * add empty digest 752 * Empty because we don't have it yet. Either it gets created during encoding 753 * (if the data is present) or has to be set externally. 754 * XXX maybe pass it in optionally? 755 */ 756 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 757 rv = SecCmsSignedDataSetDigestValue(sigd, digestalgtag, NULL); 758 if (rv != SECSuccess) 759 goto loser; 760 761 /* 762 * The last thing to get consistency would be adding the digest. 763 */ 764 765 PORT_ArenaUnmark(poolp, mark); 766 return SECSuccess; 767 768 loser: 769 PORT_ArenaRelease (poolp, mark); 770 return SECFailure; 771 } 772 773 SecAsn1Item * 774 SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd, SECOidTag algtag) 775 { 776 int idx; 777 778 if(sigd == NULL || sigd->digests == NULL) { 779 return NULL; 780 } 781 idx = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, algtag); 782 return (idx >= 0)?(sigd->digests)[idx]:NULL; 783 } 784 785 OSStatus 786 SecCmsSignedDataSetDigestContext(SecCmsSignedDataRef sigd, 787 SecCmsDigestContextRef digestContext) 788 { 789 SECAlgorithmID **digestalgs; 790 SecAsn1Item * *digests; 791 792 if (SecCmsDigestContextFinishMultiple(digestContext, &digestalgs, &digests) != SECSuccess) 793 goto loser; 794 if (SecCmsSignedDataSetDigests(sigd, digestalgs, digests) != SECSuccess) 795 goto loser; 796 797 return 0; 798 loser: 799 return PORT_GetError(); 800 } 801 802 /* 803 * SecCmsSignedDataSetDigests - set a signedData's digests member 804 * 805 * "digestalgs" - array of digest algorithm IDs 806 * "digests" - array of digests corresponding to the digest algorithms 807 */ 808 OSStatus 809 SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd, 810 SECAlgorithmID **digestalgs, 811 SecAsn1Item * *digests) 812 { 813 int cnt, i, idx; 814 815 /* Check input structure and items in structure */ 816 if (sigd == NULL || sigd->digestAlgorithms == NULL || sigd->contentInfo.cmsg == NULL || 817 sigd->contentInfo.cmsg->poolp == NULL) { 818 PORT_SetError(SEC_ERROR_INVALID_ARGS); 819 return SECFailure; 820 } 821 822 /* Since we'll generate a empty digest for content-less messages 823 whether or not they're detached, we have to avoid overwriting 824 externally set digest for detached content => return early */ 825 if (sigd->digests && sigd->digests[0]) 826 return 0; 827 828 /* we assume that the digests array is just not there yet */ 829 /* 830 PORT_Assert(sigd->digests == NULL); 831 if (sigd->digests != NULL) { 832 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 833 return SECFailure; 834 } 835 */ 836 /* now allocate one (same size as digestAlgorithms) */ 837 if (sigd->digests == NULL) { 838 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms); 839 sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *)); 840 if (sigd->digests == NULL) { 841 PORT_SetError(SEC_ERROR_NO_MEMORY); 842 return SECFailure; 843 } 844 } 845 846 for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) { 847 /* try to find the sigd's i'th digest algorithm in the array we passed in */ 848 idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]); 849 if (idx < 0) { 850 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); 851 return SECFailure; 852 } 853 854 /* found it - now set it */ 855 if ((sigd->digests[i] = SECITEM_AllocItem(sigd->contentInfo.cmsg->poolp, NULL, 0)) == NULL || 856 SECITEM_CopyItem(sigd->contentInfo.cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) 857 { 858 PORT_SetError(SEC_ERROR_NO_MEMORY); 859 return SECFailure; 860 } 861 } 862 return SECSuccess; 863 } 864 865 OSStatus 866 SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd, 867 SECOidTag digestalgtag, 868 SecAsn1Item * digestdata) 869 { 870 SecAsn1Item * digest = NULL; 871 PLArenaPool *poolp; 872 void *mark; 873 int n, cnt; 874 875 poolp = sigd->contentInfo.cmsg->poolp; 876 877 mark = PORT_ArenaMark(poolp); 878 879 880 if (digestdata) { 881 digest = (SecAsn1Item *) PORT_ArenaZAlloc(poolp,sizeof(SecAsn1Item)); 882 883 /* copy digestdata item to arena (in case we have it and are not only making room) */ 884 if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess) 885 goto loser; 886 } 887 888 /* now allocate one (same size as digestAlgorithms) */ 889 if (sigd->digests == NULL) { 890 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms); 891 sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *)); 892 if (sigd->digests == NULL) { 893 PORT_SetError(SEC_ERROR_NO_MEMORY); 894 return SECFailure; 895 } 896 } 897 898 n = -1; 899 if (sigd->digestAlgorithms != NULL) 900 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 901 902 /* if not found, add a digest */ 903 if (n < 0) { 904 if (SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess) 905 goto loser; 906 } else { 907 /* replace NULL pointer with digest item (and leak previous value) */ 908 sigd->digests[n] = digest; 909 } 910 911 PORT_ArenaUnmark(poolp, mark); 912 return SECSuccess; 913 914 loser: 915 PORT_ArenaRelease(poolp, mark); 916 return SECFailure; 917 } 918 919 OSStatus 920 SecCmsSignedDataAddDigest(PRArenaPool *poolp, 921 SecCmsSignedDataRef sigd, 922 SECOidTag digestalgtag, 923 SecAsn1Item * digest) 924 { 925 SECAlgorithmID *digestalg; 926 void *mark; 927 928 mark = PORT_ArenaMark(poolp); 929 930 digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID)); 931 if (digestalg == NULL) 932 goto loser; 933 934 if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */ 935 goto loser; 936 937 if (SecCmsArrayAdd(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess || 938 /* even if digest is NULL, add dummy to have same-size array */ 939 SecCmsArrayAdd(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess) 940 { 941 goto loser; 942 } 943 944 PORT_ArenaUnmark(poolp, mark); 945 return SECSuccess; 946 947 loser: 948 PORT_ArenaRelease(poolp, mark); 949 return SECFailure; 950 } 951 952 SecAsn1Item * 953 SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd, SECOidTag digestalgtag) 954 { 955 int n; 956 957 if (sigd->digestAlgorithms == NULL) 958 return NULL; 959 960 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 961 962 return (n < 0) ? NULL : sigd->digests[n]; 963 } 964 965 /* ============================================================================= 966 * Misc. utility functions 967 */ 968 969 /* 970 * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData. 971 * 972 * cert - base certificates that will be included 973 * include_chain - if true, include the complete cert chain for cert 974 * 975 * More certs and chains can be added via AddCertificate and AddCertChain. 976 * 977 * An error results in a return value of NULL and an error set. 978 * 979 * XXXX CRLs 980 */ 981 SecCmsSignedDataRef 982 SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg, SecCertificateRef cert, Boolean include_chain) 983 { 984 SecCmsSignedDataRef sigd; 985 void *mark; 986 PLArenaPool *poolp; 987 OSStatus rv; 988 989 poolp = cmsg->poolp; 990 mark = PORT_ArenaMark(poolp); 991 992 sigd = SecCmsSignedDataCreate(cmsg); 993 if (sigd == NULL) 994 goto loser; 995 996 /* no signerinfos, thus no digestAlgorithms */ 997 998 /* but certs */ 999 if (include_chain) { 1000 rv = SecCmsSignedDataAddCertChain(sigd, cert); 1001 } else { 1002 rv = SecCmsSignedDataAddCertificate(sigd, cert); 1003 } 1004 if (rv != SECSuccess) 1005 goto loser; 1006 1007 /* RFC2630 5.2 sez: 1008 * In the degenerate case where there are no signers, the 1009 * EncapsulatedContentInfo value being "signed" is irrelevant. In this 1010 * case, the content type within the EncapsulatedContentInfo value being 1011 * "signed" should be id-data (as defined in section 4), and the content 1012 * field of the EncapsulatedContentInfo value should be omitted. 1013 */ 1014 rv = SecCmsContentInfoSetContentData(&(sigd->contentInfo), NULL, PR_TRUE); 1015 if (rv != SECSuccess) 1016 goto loser; 1017 1018 PORT_ArenaUnmark(poolp, mark); 1019 return sigd; 1020 1021 loser: 1022 if (sigd) 1023 SecCmsSignedDataDestroy(sigd); 1024 PORT_ArenaRelease(poolp, mark); 1025 return NULL; 1026 } 1027 1028 /* TODO: 1029 * SecCmsSignerInfoGetReceiptRequest() 1030 * SecCmsSignedDataHasReceiptRequest() 1031 * easy way to iterate over signers 1032 */ 1033