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 "secitem.h" 48#include "secoid.h" 49#include "tsaTemplates.h" 50 51#include <security_asn1/secasn1.h> 52#include <security_asn1/secerr.h> 53#include <Security/SecBase.h> 54#include <CommonCrypto/CommonRandomSPI.h> 55#include <Security/SecPolicyPriv.h> 56#include <utilities/SecCFWrappers.h> 57 58#ifndef NDEBUG 59#define SIGDATA_DEBUG 1 60#endif 61 62#if SIGDATA_DEBUG 63#define dprintf(args...) printf(args) 64#else 65#define dprintf(args...) 66#endif 67 68SecCmsSignedDataRef 69SecCmsSignedDataCreate(SecCmsMessageRef cmsg) 70{ 71 void *mark; 72 SecCmsSignedDataRef sigd; 73 PLArenaPool *poolp; 74 75 poolp = cmsg->poolp; 76 77 mark = PORT_ArenaMark(poolp); 78 79 sigd = (SecCmsSignedDataRef)PORT_ArenaZAlloc (poolp, sizeof(SecCmsSignedData)); 80 if (sigd == NULL) 81 goto loser; 82 83 sigd->cmsg = cmsg; 84 85 /* signerInfos, certs, certlists, crls are all empty */ 86 /* version is set in SecCmsSignedDataFinalize() */ 87 88 PORT_ArenaUnmark(poolp, mark); 89 return sigd; 90 91loser: 92 PORT_ArenaRelease(poolp, mark); 93 return NULL; 94} 95 96void 97SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd) 98{ 99 SecCmsSignerInfoRef *signerinfos, si; 100 101 if (sigd == NULL) 102 return; 103 104 if (sigd->certs != NULL) 105 CFRelease(sigd->certs); 106 107 signerinfos = sigd->signerInfos; 108 if (signerinfos != NULL) { 109 while ((si = *signerinfos++) != NULL) 110 SecCmsSignerInfoDestroy(si); 111 } 112 113 /* everything's in a pool, so don't worry about the storage */ 114 SecCmsContentInfoDestroy(&(sigd->contentInfo)); 115} 116 117/* 118 * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData 119 * before start of encoding. 120 * 121 * In detail: 122 * - find out about the right value to put into sigd->version 123 * - come up with a list of digestAlgorithms (which should be the union of the algorithms 124 * in the signerinfos). 125 * If we happen to have a pre-set list of algorithms (and digest values!), we 126 * check if we have all the signerinfos' algorithms. If not, this is an error. 127 */ 128OSStatus 129SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd) 130{ 131 SecCmsSignerInfoRef signerinfo; 132 SECOidTag digestalgtag; 133 CSSM_DATA_PTR dummy; 134 int version; 135 OSStatus rv; 136 Boolean haveDigests = PR_FALSE; 137 int n, i; 138 PLArenaPool *poolp; 139 140 poolp = sigd->cmsg->poolp; 141 142 /* we assume that we have precomputed digests if there is a list of algorithms, and */ 143 /* a chunk of data for each of those algorithms */ 144 if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) { 145 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) { 146 if (sigd->digests[i] == NULL) 147 break; 148 } 149 if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */ 150 haveDigests = PR_TRUE; /* yes: we must have all the digests */ 151 } 152 153 version = SEC_CMS_SIGNED_DATA_VERSION_BASIC; 154 155 /* RFC2630 5.1 "version is the syntax version number..." */ 156 if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA) 157 version = SEC_CMS_SIGNED_DATA_VERSION_EXT; 158 159 /* prepare all the SignerInfos (there may be none) */ 160 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { 161 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); 162 163 /* RFC2630 5.1 "version is the syntax version number..." */ 164 if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN) 165 version = SEC_CMS_SIGNED_DATA_VERSION_EXT; 166 167 /* collect digestAlgorithms from SignerInfos */ 168 /* (we need to know which algorithms we have when the content comes in) */ 169 /* do not overwrite any existing digestAlgorithms (and digest) */ 170 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 171 172 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 173 if (n < 0 && haveDigests) { 174 /* oops, there is a digestalg we do not have a digest for */ 175 /* but we were supposed to have all the digests already... */ 176 goto loser; 177 } else if (n < 0) { 178 /* add the digestAlgorithm & a NULL digest */ 179 rv = SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, NULL); 180 if (rv != SECSuccess) 181 goto loser; 182 } else { 183 /* found it, nothing to do */ 184 } 185 } 186 187 dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version); 188 if (dummy == NULL) 189 return SECFailure; 190 191 /* this is a SET OF, so we need to sort them guys */ 192 rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms, 193 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), 194 (void **)sigd->digests); 195 if (rv != SECSuccess) 196 return SECFailure; 197 198 return SECSuccess; 199 200loser: 201 return SECFailure; 202} 203 204OSStatus 205SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd) 206{ 207 /* set up the digests */ 208 if (sigd->digestAlgorithms != NULL) { 209 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms); 210 if (sigd->contentInfo.digcx == NULL) 211 return SECFailure; 212 } 213 return SECSuccess; 214} 215 216#include <AssertMacros.h> 217#include "tsaSupport.h" 218#include "tsaSupportPriv.h" 219#include "tsaTemplates.h" 220#include <security_keychain/tsaDERUtilities.h> 221 222extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate; 223 224OSStatus createTSAMessageImprint(SecCmsSignedDataRef signedData, CSSM_DATA_PTR encDigest, 225 SecAsn1TSAMessageImprint *messageImprint) 226{ 227 // Calculate hash of encDigest and put in messageImprint.hashedMessage 228 // We pass in encDigest, since in the verification case, it comes from a different signedData 229 230 OSStatus status = SECFailure; 231 232 require(signedData && messageImprint, xit); 233 234 SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData); 235 require(digestAlgorithms, xit); 236 237 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms); 238 require(digcx, xit); 239 require(encDigest, xit); 240 241 SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(signedData, 0); // NB - assume 1 signer only! 242 messageImprint->hashAlgorithm = signerinfo->digestAlg; 243 244 SecCmsDigestContextUpdate(digcx, encDigest->Data, encDigest->Length); 245 246 require_noerr(SecCmsDigestContextFinishSingle(digcx, (SecArenaPoolRef)signedData->cmsg->poolp, 247 &messageImprint->hashedMessage), xit); 248 249 status = SECSuccess; 250xit: 251 return status; 252} 253 254#include <Security/SecAsn1Templates.h> 255 256#ifndef NDEBUG 257static OSStatus decodeDERUTF8String(const CSSM_DATA_PTR content, char *statusstr, size_t strsz) 258{ 259 // The statusString should use kSecAsn1SequenceOfUTF8StringTemplate, but doesn't 260 OSStatus status = SECFailure; 261 SecAsn1CoderRef coder = NULL; 262 CSSM_DATA statusString; 263 size_t len = 0; 264 265 require(content && statusstr, xit); 266 267 require_noerr(SecAsn1CoderCreate(&coder), xit); 268 require_noerr(SecAsn1Decode(coder, content->Data, content->Length, kSecAsn1UTF8StringTemplate, &statusString), xit); 269 status = 0; 270 len = (statusString.Length < strsz)?(int)statusString.Length:strsz; 271 if (statusstr && statusString.Data) 272 strncpy(statusstr, (const char *)statusString.Data, len); 273 274xit: 275 if (coder) 276 SecAsn1CoderRelease(coder); 277 return status; 278} 279#endif 280 281static OSStatus validateTSAResponseAndAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR tsaResponse, 282 uint64_t expectedNonce) 283{ 284 OSStatus status = SECFailure; 285 SecAsn1CoderRef coder = NULL; 286 SecAsn1TimeStampRespDER respDER = {{{0}},}; 287 SecAsn1TSAPKIStatusInfo *tsastatus = NULL; 288 int respstatus = -1; 289#ifndef NDEBUG 290 int failinfo = -1; 291#endif 292 293 require_action(tsaResponse && tsaResponse->Data && tsaResponse->Length, xit, status = errSecTimestampMissing); 294 295 require_noerr(SecAsn1CoderCreate(&coder), xit); 296 require_noerr(SecTSAResponseCopyDEREncoding(coder, tsaResponse, &respDER), xit); 297 298#ifndef NDEBUG 299 tsaWriteFileX("/tmp/tsa-timeStampToken.der", respDER.timeStampTokenDER.Data, respDER.timeStampTokenDER.Length); 300#endif 301 302 tsastatus = (SecAsn1TSAPKIStatusInfo *)&respDER.status; 303 require_action(tsastatus->status.Data, xit, status = errSecTimestampBadDataFormat); 304 respstatus = (int)tsaDER_ToInt(&tsastatus->status); 305 306#ifndef NDEBUG 307 char buf[80]={0,}; 308 if (tsastatus->failInfo.Data) // e.g. FI_BadRequest 309 failinfo = (int)tsaDER_ToInt(&tsastatus->failInfo); 310 311 if (tsastatus->statusString.Data && tsastatus->statusString.Length) 312 { 313 OSStatus strrx = decodeDERUTF8String(&tsastatus->statusString, buf, sizeof(buf)); 314 dprintf("decodeDERUTF8String status: %d\n", (int)strrx); 315 } 316 317 dprintf("validateTSAResponse: status: %d, failinfo: %d, %s\n", respstatus, failinfo, buf); 318#endif 319 320 switch (respstatus) 321 { 322 case PKIS_Granted: 323 case PKIS_GrantedWithMods: // Success 324 status = noErr; 325 break; 326 case PKIS_Rejection: 327 status = errSecTimestampRejection; 328 break; 329 case PKIS_Waiting: 330 status = errSecTimestampWaiting; 331 break; 332 case PKIS_RevocationWarning: 333 status = errSecTimestampRevocationWarning; 334 break; 335 case PKIS_RevocationNotification: 336 status = errSecTimestampRevocationNotification; 337 break; 338 default: 339 status = errSecTimestampSystemFailure; 340 break; 341 } 342 require_noerr(status, xit); 343 344 // If we succeeded, then we must have a TimeStampToken 345 346 require_action(respDER.timeStampTokenDER.Data && respDER.timeStampTokenDER.Length, xit, status = errSecTimestampBadDataFormat); 347 348 dprintf("timestamp full expected nonce: %lld\n", expectedNonce); 349 350 /* 351 The bytes in respDER are a full CMS message, which we need to check now for validity. 352 The code for this is essentially the same code taht is done during a timestamp 353 verify, except that we also need to check the nonce. 354 */ 355 require_noerr(status = decodeTimeStampToken(signerinfo, &respDER.timeStampTokenDER, NULL, expectedNonce), xit); 356 357 status = SecCmsSignerInfoAddTimeStamp(signerinfo, &respDER.timeStampTokenDER); 358 359xit: 360 if (coder) 361 SecAsn1CoderRelease(coder); 362 return status; 363} 364 365static OSStatus getRandomNonce(uint64_t *nonce) 366{ 367 return nonce ? CCRandomCopyBytes(kCCRandomDevRandom, (void *)nonce, sizeof(*nonce)) : SECFailure; 368} 369 370static OSStatus remapTimestampError(OSStatus inStatus) 371{ 372 /* 373 Since communicating with the timestamp server is perhaps an unexpected 374 dependency on the network, map unknown errors into something to indicate 375 that signing without a timestamp may be a workaround. In particular, the 376 private CFURL errors (see CFNetworkErrorsPriv.i) 377 378 kCFURLErrorTimedOut = -1001, 379 kCFURLErrorNotConnectedToInternet = -1009, 380 381 are remapped to errSecTimestampServiceNotAvailable. 382 */ 383 384 switch (inStatus) 385 { 386 case errSecTimestampMissing: 387 case errSecTimestampInvalid: 388 case errSecTimestampNotTrusted: 389 case errSecTimestampServiceNotAvailable: 390 case errSecTimestampBadAlg: 391 case errSecTimestampBadRequest: 392 case errSecTimestampBadDataFormat: 393 case errSecTimestampTimeNotAvailable: 394 case errSecTimestampUnacceptedPolicy: 395 case errSecTimestampUnacceptedExtension: 396 case errSecTimestampAddInfoNotAvailable: 397 case errSecTimestampSystemFailure: 398 case errSecSigningTimeMissing: 399 case errSecTimestampRejection: 400 case errSecTimestampWaiting: 401 case errSecTimestampRevocationWarning: 402 case errSecTimestampRevocationNotification: 403 return inStatus; 404 default: 405 return errSecTimestampServiceNotAvailable; 406 } 407 return errSecTimestampServiceNotAvailable; 408} 409 410/* 411 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData 412 * after all the encapsulated data was passed through the encoder. 413 * 414 * In detail: 415 * - create the signatures in all the SignerInfos 416 * 417 * Please note that nothing is done to the Certificates and CRLs in the message - this 418 * is entirely the responsibility of our callers. 419 */ 420OSStatus 421SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd) 422{ 423 SecCmsSignerInfoRef *signerinfos, signerinfo; 424 SecCmsContentInfoRef cinfo; 425 SECOidTag digestalgtag; 426 OSStatus ret = SECFailure; 427 OSStatus rv; 428 CSSM_DATA_PTR contentType; 429 int certcount; 430 int i, ci, n, rci, si; 431 PLArenaPool *poolp; 432 CFArrayRef certlist; 433 extern const SecAsn1Template SecCmsSignerInfoTemplate[]; 434 435 poolp = sigd->cmsg->poolp; 436 cinfo = &(sigd->contentInfo); 437 438 /* did we have digest calculation going on? */ 439 if (cinfo->digcx) { 440 rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests)); 441 if (rv != SECSuccess) 442 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ 443 cinfo->digcx = NULL; 444 } 445 446 signerinfos = sigd->signerInfos; 447 certcount = 0; 448 449 /* prepare all the SignerInfos (there may be none) */ 450 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { 451 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); 452 453 /* find correct digest for this signerinfo */ 454 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 455 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 456 if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) { 457 /* oops - digest not found */ 458 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); 459 goto loser; 460 } 461 462 /* XXX if our content is anything else but data, we need to force the 463 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a 464 * collection...") */ 465 466 /* pass contentType here as we want a contentType attribute */ 467 if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL) 468 goto loser; 469 470 /* sign the thing */ 471 rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType); 472 if (rv != SECSuccess) 473 goto loser; 474 475 /* while we're at it, count number of certs in certLists */ 476 certlist = SecCmsSignerInfoGetCertList(signerinfo); 477 if (certlist) 478 certcount += CFArrayGetCount(certlist); 479 } 480 481 /* Now we can get a timestamp, since we have all the digests */ 482 483 // We force the setting of a callback, since this is the most usual case 484 if (!sigd->cmsg->tsaCallback) 485 SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback); 486 487 if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext) 488 { 489 CSSM_DATA tsaResponse = {0,}; 490 SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}}; 491 // <rdar://problem/11073466> Add nonce support for timestamping client 492 493 uint64_t nonce = 0; 494 495 require_noerr(getRandomNonce(&nonce), tsxit); 496 dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd)); 497 498 // Calculate hash of encDigest and put in messageImprint.hashedMessage 499 SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0); // NB - assume 1 signer only! 500 CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo); 501 require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit); 502 503 // Callback to fire up XPC service to talk to TimeStamping server, etc. 504 require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint, 505 nonce, &tsaResponse), tsxit); 506 507 require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit); 508 509 /* 510 It is likely that every occurrence of "goto loser" in this file should 511 also do a PORT_SetError. Since it is not clear what might depend on this 512 behavior, we just do this in the timestamping case. 513 */ 514tsxit: 515 if (rv) 516 { 517 dprintf("Original timestamp error: %d\n", (int)rv); 518 rv = remapTimestampError(rv); 519 PORT_SetError(rv); 520 goto loser; 521 } 522 } 523 524 /* this is a SET OF, so we need to sort them guys */ 525 rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL); 526 if (rv != SECSuccess) 527 goto loser; 528 529 /* 530 * now prepare certs & crls 531 */ 532 533 /* count the rest of the certs */ 534 if (sigd->certs != NULL) 535 certcount += CFArrayGetCount(sigd->certs); 536 537 if (certcount == 0) { 538 sigd->rawCerts = NULL; 539 } else { 540 /* 541 * Combine all of the certs and cert chains into rawcerts. 542 * Note: certcount is an upper bound; we may not need that many slots 543 * but we will allocate anyway to avoid having to do another pass. 544 * (The temporary space saving is not worth it.) 545 * 546 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent 547 * SetOfDERcertficates implementation 548 */ 549 sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR)); 550 if (sigd->rawCerts == NULL) 551 return SECFailure; 552 553 /* 554 * XXX Want to check for duplicates and not add *any* cert that is 555 * already in the set. This will be more important when we start 556 * dealing with larger sets of certs, dual-key certs (signing and 557 * encryption), etc. For the time being we can slide by... 558 * 559 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent 560 * SetOfDERcertficates implementation 561 */ 562 rci = 0; 563 if (signerinfos != NULL) { 564 for (si = 0; signerinfos[si] != NULL; si++) { 565 signerinfo = signerinfos[si]; 566 for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) { 567 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); 568 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci); 569 SecCertificateGetData(cert, sigd->rawCerts[rci++]); 570 } 571 } 572 } 573 574 if (sigd->certs != NULL) { 575 for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) { 576 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); 577 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci); 578 SecCertificateGetData(cert, sigd->rawCerts[rci++]); 579 } 580 } 581 582 sigd->rawCerts[rci] = NULL; 583 584 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ 585 SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); 586 } 587 588 ret = SECSuccess; 589 590loser: 591 592 dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv); 593 return ret; 594} 595 596OSStatus 597SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd) 598{ 599 /* set up the digests */ 600 if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) { 601 /* if digests are already there, do nothing */ 602 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms); 603 if (sigd->contentInfo.digcx == NULL) 604 return SECFailure; 605 } 606 return SECSuccess; 607} 608 609/* 610 * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData 611 * after all the encapsulated data was passed through the decoder. 612 */ 613OSStatus 614SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd) 615{ 616 /* did we have digest calculation going on? */ 617 if (sigd->contentInfo.digcx) { 618 if (SecCmsDigestContextFinishMultiple(sigd->contentInfo.digcx, (SecArenaPoolRef)sigd->cmsg->poolp, &(sigd->digests)) != SECSuccess) 619 return SECFailure; /* error has been set by SecCmsDigestContextFinishMultiple */ 620 sigd->contentInfo.digcx = NULL; 621 } 622 return SECSuccess; 623} 624 625/* 626 * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData 627 * after all decoding is finished. 628 */ 629OSStatus 630SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd) 631{ 632 SecCmsSignerInfoRef *signerinfos; 633 int i; 634 635 signerinfos = sigd->signerInfos; 636 637 /* set cmsg and sigd backpointers for all the signerinfos */ 638 if (signerinfos) { 639 for (i = 0; signerinfos[i] != NULL; i++) { 640 signerinfos[i]->cmsg = sigd->cmsg; 641 signerinfos[i]->sigd = sigd; 642 } 643 } 644 645 return SECSuccess; 646} 647 648/* 649 * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list 650 */ 651SecCmsSignerInfoRef * 652SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd) 653{ 654 return sigd->signerInfos; 655} 656 657int 658SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd) 659{ 660 return SecCmsArrayCount((void **)sigd->signerInfos); 661} 662 663SecCmsSignerInfoRef 664SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd, int i) 665{ 666 return sigd->signerInfos[i]; 667} 668 669/* 670 * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list 671 */ 672SECAlgorithmID ** 673SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd) 674{ 675 return sigd->digestAlgorithms; 676} 677 678/* 679 * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo 680 */ 681SecCmsContentInfoRef 682SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd) 683{ 684 return &(sigd->contentInfo); 685} 686 687/* 688 * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list 689 */ 690CSSM_DATA_PTR * 691SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd) 692{ 693 return sigd->rawCerts; 694} 695 696OSStatus 697SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain, 698 SECCertUsage certusage, Boolean keepcerts) 699{ 700 int certcount; 701 OSStatus rv; 702 int i; 703 704 certcount = SecCmsArrayCount((void **)sigd->rawCerts); 705 706 rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL, 707 keepcerts, PR_FALSE, NULL); 708 709 /* XXX CRL handling */ 710 711 if (sigd->signerInfos != NULL) { 712 /* fill in all signerinfo's certs */ 713 for (i = 0; sigd->signerInfos[i] != NULL; i++) 714 (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[i], keychain); 715 } 716 717 return rv; 718} 719 720/* 721 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case 722 * of external signatures! 723 */ 724 725/* 726 * SecCmsSignedDataVerifySignerInfo - check the signatures. 727 * 728 * The digests were either calculated during decoding (and are stored in the 729 * signedData itself) or set after decoding using SecCmsSignedDataSetDigests. 730 * 731 * The verification checks if the signing cert is valid and has a trusted chain 732 * for the purpose specified by "policies". 733 * 734 * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly. 735 * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate(). 736 */ 737OSStatus 738SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i, 739 SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef) 740{ 741 SecCmsSignerInfoRef signerinfo; 742 SecCmsContentInfoRef cinfo; 743 SECOidData *algiddata; 744 CSSM_DATA_PTR contentType, digest; 745 OSStatus status, status2; 746 747 cinfo = &(sigd->contentInfo); 748 749 signerinfo = sigd->signerInfos[i]; 750 751 /* Signature or digest level verificationStatus errors should supercede 752 certificate level errors, so check the digest and signature first. */ 753 754 /* Find digest and contentType for signerinfo */ 755 algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo); 756 if (algiddata == NULL) { 757 return errSecInternalError; // shouldn't have happened, this is likely due to corrupted data 758 } 759 760 digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset); 761 if(digest == NULL) { 762 /* 763 * No digests; this probably had detached content the caller has to 764 * deal with. 765 * FIXME: need some error return for this (as well as many 766 * other places in this library). 767 */ 768 return errSecDataNotAvailable; 769 } 770 contentType = SecCmsContentInfoGetContentTypeOID(cinfo); 771 772 /* verify signature */ 773 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); 774 status = SecCmsSignerInfoVerifyWithPolicy(signerinfo, timeStampPolicies, digest, contentType); 775 CFReleaseSafe(timeStampPolicies); 776 777 /* Now verify the certificate. We do this even if the signature failed to verify so we can 778 return a trustRef to the caller for display purposes. */ 779 status2 = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray, 780 policies, trustRef); 781 dprintf("SecCmsSignedDataVerifySignerInfo: status %d status2 %d\n", (int) status, (int)status2); 782 /* The error from SecCmsSignerInfoVerify() supercedes error from SecCmsSignerInfoVerifyCertificate(). */ 783 if (status) 784 return status; 785 786 return status2; 787} 788 789/* 790 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message 791 */ 792OSStatus 793SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 794 SecKeychainRef keychainOrArray, 795 CFTypeRef policies) 796{ 797 SecCertificateRef cert; 798 OSStatus rv = SECSuccess; 799 int i; 800 int count; 801 802 if (!sigd || !keychainOrArray || !sigd->rawCerts) { 803 PORT_SetError(SEC_ERROR_INVALID_ARGS); 804 return SECFailure; 805 } 806 807 count = SecCmsArrayCount((void**)sigd->rawCerts); 808 for (i=0; i < count; i++) { 809 if (sigd->certs && CFArrayGetCount(sigd->certs) > i) { 810 cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i); 811 CFRetain(cert); 812 } else { 813 cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]); 814 if (!cert) { 815 rv = SECFailure; 816 break; 817 } 818 } 819 rv |= CERT_VerifyCert(keychainOrArray, cert, sigd->rawCerts, 820 policies, CFAbsoluteTimeGetCurrent(), NULL); 821 CFRelease(cert); 822 } 823 824 return rv; 825} 826 827/* 828 * SecCmsSignedDataHasDigests - see if we have digests in place 829 */ 830Boolean 831SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd) 832{ 833 return (sigd->digests != NULL); 834} 835 836OSStatus 837SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd, CFArrayRef certlist) 838{ 839 PORT_Assert(certlist != NULL); 840 841 if (certlist == NULL) 842 return SECFailure; 843 844 if (!sigd->certs) 845 sigd->certs = CFArrayCreateMutableCopy(NULL, 0, certlist); 846 else 847 { 848 CFRange certlistRange = { 0, CFArrayGetCount(certlist) }; 849 CFArrayAppendArray(sigd->certs, certlist, certlistRange); 850 } 851 852 return SECSuccess; 853} 854 855/* 856 * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs 857 */ 858OSStatus 859SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd, SecCertificateRef cert) 860{ 861 CFArrayRef certlist; 862 SECCertUsage usage; 863 OSStatus rv; 864 865 usage = certUsageEmailSigner; 866 867 /* do not include root */ 868 certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE); 869 if (certlist == NULL) 870 return SECFailure; 871 872 rv = SecCmsSignedDataAddCertList(sigd, certlist); 873 CFRelease(certlist); 874 875 return rv; 876} 877 878OSStatus 879SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd, SecCertificateRef cert) 880{ 881 PORT_Assert(cert != NULL); 882 883 if (cert == NULL) 884 return SECFailure; 885 886 if (!sigd->certs) 887 sigd->certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 888 889 CFArrayAppendValue(sigd->certs, cert); 890 891 return SECSuccess; 892} 893 894Boolean 895SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd) 896{ 897 if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL) 898 return PR_TRUE; 899 else if (sigd->rawCrls != NULL && sigd->rawCrls[0] != NULL) 900 return PR_TRUE; 901 else 902 return PR_FALSE; 903} 904 905OSStatus 906SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd, 907 SecCmsSignerInfoRef signerinfo) 908{ 909 void *mark; 910 OSStatus rv; 911 SECOidTag digestalgtag; 912 PLArenaPool *poolp; 913 914 poolp = sigd->cmsg->poolp; 915 916 mark = PORT_ArenaMark(poolp); 917 918 /* add signerinfo */ 919 rv = SecCmsArrayAdd(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo); 920 if (rv != SECSuccess) 921 goto loser; 922 923 signerinfo->sigd = sigd; 924 925 /* 926 * add empty digest 927 * Empty because we don't have it yet. Either it gets created during encoding 928 * (if the data is present) or has to be set externally. 929 * XXX maybe pass it in optionally? 930 */ 931 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); 932 rv = SecCmsSignedDataSetDigestValue(sigd, digestalgtag, NULL); 933 if (rv != SECSuccess) 934 goto loser; 935 936 /* 937 * The last thing to get consistency would be adding the digest. 938 */ 939 940 PORT_ArenaUnmark(poolp, mark); 941 return SECSuccess; 942 943loser: 944 PORT_ArenaRelease (poolp, mark); 945 return SECFailure; 946} 947 948CSSM_DATA_PTR 949SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd, SECOidTag algtag) 950{ 951 int idx; 952 953 if(sigd->digests == NULL) { 954 return NULL; 955 } 956 idx = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, algtag); 957 return sigd->digests[idx]; 958} 959 960/* 961 * SecCmsSignedDataSetDigests - set a signedData's digests member 962 * 963 * "digestalgs" - array of digest algorithm IDs 964 * "digests" - array of digests corresponding to the digest algorithms 965 */ 966OSStatus 967SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd, 968 SECAlgorithmID **digestalgs, 969 CSSM_DATA_PTR *digests) 970{ 971 int cnt, i, idx; 972 973 if (sigd->digestAlgorithms == NULL) { 974 PORT_SetError(SEC_ERROR_INVALID_ARGS); 975 return SECFailure; 976 } 977 978 /* we assume that the digests array is just not there yet */ 979 PORT_Assert(sigd->digests == NULL); 980 if (sigd->digests != NULL) { 981 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 982 return SECFailure; 983 } 984 985 /* now allocate one (same size as digestAlgorithms) */ 986 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms); 987 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR)); 988 if (sigd->digests == NULL) { 989 PORT_SetError(SEC_ERROR_NO_MEMORY); 990 return SECFailure; 991 } 992 993 for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) { 994 /* try to find the sigd's i'th digest algorithm in the array we passed in */ 995 idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]); 996 if (idx < 0) { 997 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); 998 return SECFailure; 999 } 1000 1001 /* found it - now set it */ 1002 if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL || 1003 SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) 1004 { 1005 PORT_SetError(SEC_ERROR_NO_MEMORY); 1006 return SECFailure; 1007 } 1008 } 1009 return SECSuccess; 1010} 1011 1012OSStatus 1013SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd, 1014 SECOidTag digestalgtag, 1015 CSSM_DATA_PTR digestdata) 1016{ 1017 CSSM_DATA_PTR digest = NULL; 1018 PLArenaPool *poolp; 1019 void *mark; 1020 int n, cnt; 1021 1022 poolp = sigd->cmsg->poolp; 1023 1024 mark = PORT_ArenaMark(poolp); 1025 1026 1027 if (digestdata) { 1028 digest = (CSSM_DATA_PTR) PORT_ArenaZAlloc(poolp,sizeof(CSSM_DATA)); 1029 1030 /* copy digestdata item to arena (in case we have it and are not only making room) */ 1031 if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess) 1032 goto loser; 1033 } 1034 1035 /* now allocate one (same size as digestAlgorithms) */ 1036 if (sigd->digests == NULL) { 1037 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms); 1038 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR)); 1039 if (sigd->digests == NULL) { 1040 PORT_SetError(SEC_ERROR_NO_MEMORY); 1041 return SECFailure; 1042 } 1043 } 1044 1045 n = -1; 1046 if (sigd->digestAlgorithms != NULL) 1047 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 1048 1049 /* if not found, add a digest */ 1050 if (n < 0) { 1051 if (SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, digest) != SECSuccess) 1052 goto loser; 1053 } else { 1054 /* replace NULL pointer with digest item (and leak previous value) */ 1055 sigd->digests[n] = digest; 1056 } 1057 1058 PORT_ArenaUnmark(poolp, mark); 1059 return SECSuccess; 1060 1061loser: 1062 PORT_ArenaRelease(poolp, mark); 1063 return SECFailure; 1064} 1065 1066OSStatus 1067SecCmsSignedDataAddDigest(SecArenaPoolRef pool, 1068 SecCmsSignedDataRef sigd, 1069 SECOidTag digestalgtag, 1070 CSSM_DATA_PTR digest) 1071{ 1072 PRArenaPool *poolp = (PRArenaPool *)pool; 1073 SECAlgorithmID *digestalg; 1074 void *mark; 1075 1076 mark = PORT_ArenaMark(poolp); 1077 1078 digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID)); 1079 if (digestalg == NULL) 1080 goto loser; 1081 1082 if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */ 1083 goto loser; 1084 1085 if (SecCmsArrayAdd(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess || 1086 /* even if digest is NULL, add dummy to have same-size array */ 1087 SecCmsArrayAdd(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess) 1088 { 1089 goto loser; 1090 } 1091 1092 PORT_ArenaUnmark(poolp, mark); 1093 return SECSuccess; 1094 1095loser: 1096 PORT_ArenaRelease(poolp, mark); 1097 return SECFailure; 1098} 1099 1100CSSM_DATA_PTR 1101SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd, SECOidTag digestalgtag) 1102{ 1103 int n; 1104 1105 if (sigd->digestAlgorithms == NULL) 1106 return NULL; 1107 1108 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); 1109 1110 return (n < 0) ? NULL : sigd->digests[n]; 1111} 1112 1113/* ============================================================================= 1114 * Misc. utility functions 1115 */ 1116 1117/* 1118 * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData. 1119 * 1120 * cert - base certificates that will be included 1121 * include_chain - if true, include the complete cert chain for cert 1122 * 1123 * More certs and chains can be added via AddCertificate and AddCertChain. 1124 * 1125 * An error results in a return value of NULL and an error set. 1126 * 1127 * XXXX CRLs 1128 */ 1129SecCmsSignedDataRef 1130SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg, SecCertificateRef cert, Boolean include_chain) 1131{ 1132 SecCmsSignedDataRef sigd; 1133 void *mark; 1134 PLArenaPool *poolp; 1135 OSStatus rv; 1136 1137 poolp = cmsg->poolp; 1138 mark = PORT_ArenaMark(poolp); 1139 1140 sigd = SecCmsSignedDataCreate(cmsg); 1141 if (sigd == NULL) 1142 goto loser; 1143 1144 /* no signerinfos, thus no digestAlgorithms */ 1145 1146 /* but certs */ 1147 if (include_chain) { 1148 rv = SecCmsSignedDataAddCertChain(sigd, cert); 1149 } else { 1150 rv = SecCmsSignedDataAddCertificate(sigd, cert); 1151 } 1152 if (rv != SECSuccess) 1153 goto loser; 1154 1155 /* RFC2630 5.2 sez: 1156 * In the degenerate case where there are no signers, the 1157 * EncapsulatedContentInfo value being "signed" is irrelevant. In this 1158 * case, the content type within the EncapsulatedContentInfo value being 1159 * "signed" should be id-data (as defined in section 4), and the content 1160 * field of the EncapsulatedContentInfo value should be omitted. 1161 */ 1162 rv = SecCmsContentInfoSetContentData(cmsg, &(sigd->contentInfo), NULL, PR_TRUE); 1163 if (rv != SECSuccess) 1164 goto loser; 1165 1166 PORT_ArenaUnmark(poolp, mark); 1167 return sigd; 1168 1169loser: 1170 if (sigd) 1171 SecCmsSignedDataDestroy(sigd); 1172 PORT_ArenaRelease(poolp, mark); 1173 return NULL; 1174} 1175 1176/* 1177 * Get SecCmsSignedDataRawCerts - obtain raw certs as a NULL_terminated array 1178 * of pointers. 1179 */ 1180extern OSStatus SecCmsSignedDataRawCerts(SecCmsSignedDataRef sigd, 1181 CSSM_DATA_PTR **rawCerts) 1182{ 1183 *rawCerts = sigd->rawCerts; 1184 return noErr; 1185} 1186 1187/* TODO: 1188 * SecCmsSignerInfoGetReceiptRequest() 1189 * SecCmsSignedDataHasReceiptRequest() 1190 * easy way to iterate over signers 1191 */ 1192 1193