CMSEncoder.c
1 /* 2 * Copyright (c) 2006-2016 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 /* 25 * CMSEncoder.c - encode, sign, and/or encrypt CMS messages. 26 */ 27 28 #include <Security/CMSEncoder.h> 29 #include <Security/CMSPrivate.h> 30 #include "CMSUtils.h" 31 #include <Security/SecBase.h> 32 #include <Security/SecCmsEncoder.h> 33 #include <Security/SecCmsEnvelopedData.h> 34 #include <Security/SecCmsMessage.h> 35 #include <Security/SecCmsRecipientInfo.h> 36 #include <Security/SecCmsSignedData.h> 37 #include <Security/SecCmsSignerInfo.h> 38 #include <Security/SecCmsContentInfo.h> 39 #include <Security/SecCertificate.h> 40 #include <Security/SecIdentity.h> 41 #include <Security/SecSMIME.h> 42 #include <Security/oidsattr.h> 43 #include <Security/SecAsn1Coder.h> 44 #include <Security/SecAsn1Types.h> 45 #include <Security/SecAsn1Templates.h> 46 #include <CoreFoundation/CFRuntime.h> 47 #include <pthread.h> 48 #include <utilities/SecCFRelease.h> 49 50 #include <security_smime/cmspriv.h> 51 52 #if TIMESTAMPING_SUPPORTED 53 #include <security_smime/tsaSupport.h> 54 #endif 55 56 #pragma mark --- Private types and definitions --- 57 58 /* 59 * Encoder state. 60 */ 61 typedef enum { 62 ES_Init, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent 63 * and CMSEncodeGetCmsMessage */ 64 ES_Msg, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */ 65 ES_Updating, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */ 66 ES_Final /* CMSEncoderCopyEncodedContent has been called */ 67 } CMSEncoderState; 68 69 /* 70 * High-level operation: what are we doing? 71 */ 72 typedef enum { 73 EO_Sign, 74 EO_Encrypt, 75 EO_SignEncrypt 76 } CMSEncoderOp; 77 78 /* 79 * Caller's CMSEncoderRef points to one of these. 80 */ 81 struct _CMSEncoder { 82 CFRuntimeBase base; 83 CMSEncoderState encState; 84 CMSEncoderOp op; 85 Boolean detachedContent; 86 SecAsn1Oid eContentType; 87 CFMutableArrayRef signers; 88 CFMutableArrayRef recipients; 89 CFMutableArrayRef otherCerts; 90 CMSSignedAttributes signedAttributes; 91 CFAbsoluteTime signingTime; 92 SecCmsMessageRef cmsMsg; /* the encoder's arena */ 93 SecCmsEncoderRef encoder; 94 CFMutableDataRef encoderOut; /* output goes here... */ 95 bool customCoder; /* unless this is set by 96 * CMSEncoderSetEncoder */ 97 SECOidTag digestalgtag; 98 99 CMSCertificateChainMode chainMode; 100 CFDataRef hashAgilityAttrValue; 101 CFDictionaryRef hashAgilityV2AttrValues; 102 CFAbsoluteTime expirationTime; 103 }; 104 105 static void cmsEncoderInit(CFTypeRef enc); 106 static void cmsEncoderFinalize(CFTypeRef enc); 107 108 static CFRuntimeClass cmsEncoderRuntimeClass = 109 { 110 0, /* version */ 111 "CMSEncoder", 112 cmsEncoderInit, 113 NULL, /* copy */ 114 cmsEncoderFinalize, 115 NULL, /* equal - just use pointer equality */ 116 NULL, /* hash, ditto */ 117 NULL, /* copyFormattingDesc */ 118 NULL /* copyDebugDesc */ 119 }; 120 121 #if TIMESTAMPING_SUPPORTED 122 void CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback); 123 #endif 124 125 #pragma mark --- Private routines --- 126 127 /* 128 * Decode a CFStringRef representation of an integer 129 */ 130 static int cfStringToNumber( 131 CFStringRef inStr) 132 { 133 int max = 32; 134 char buf[max]; 135 if (!inStr || !CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII)) 136 return -1; 137 return atoi(buf); 138 } 139 140 /* 141 * Encode an integer component of an OID, return resulting number of bytes; 142 * actual bytes are mallocd and returned in *encodeArray. 143 */ 144 static unsigned encodeNumber( 145 int num, 146 unsigned char **encodeArray) // mallocd and RETURNED 147 { 148 unsigned char *result; 149 unsigned dex; 150 unsigned numDigits = 0; 151 unsigned scratch; 152 153 /* trival case - 0 maps to 0 */ 154 if(num == 0) { 155 *encodeArray = (unsigned char *)malloc(1); 156 **encodeArray = 0; 157 return 1; 158 } 159 160 /* first calculate the number of digits in num, base 128 */ 161 scratch = (unsigned)num; 162 while(scratch != 0) { 163 numDigits++; 164 scratch >>= 7; 165 } 166 167 result = (unsigned char *)malloc(numDigits); 168 scratch = (unsigned)num; 169 for(dex=0; dex<numDigits; dex++) { 170 result[numDigits - dex - 1] = scratch & 0x7f; 171 scratch >>= 7; 172 } 173 174 /* all digits except the last one have m.s. bit set */ 175 for(dex=0; dex<(numDigits - 1); dex++) { 176 result[dex] |= 0x80; 177 } 178 179 *encodeArray = result; 180 return numDigits; 181 } 182 183 /* 184 * Given an OID in dotted-decimal string representation, convert to binary 185 * DER format. Returns a pointer in outOid which the caller must free(), 186 * as well as the length of the data in outLen. 187 * Function returns 0 if successful, non-zero otherwise. 188 */ 189 static int encodeOid( 190 const unsigned char *inStr, 191 unsigned char **outOid, 192 unsigned int *outLen) 193 { 194 unsigned char **digits = NULL; /* array of char * from encodeNumber */ 195 unsigned *numDigits = NULL; /* array of unsigned from encodeNumber */ 196 CFIndex digit; 197 unsigned numDigitBytes; /* total #of output chars */ 198 unsigned char firstByte; 199 unsigned char *outP; 200 CFIndex numsToProcess; 201 CFStringRef oidStr = NULL; 202 CFArrayRef argvRef = NULL; 203 int num, result = 1; 204 CFIndex argc; 205 206 /* parse input string into array of substrings */ 207 if (!inStr || !outOid || !outLen) goto cleanExit; 208 oidStr = CFStringCreateWithCString(NULL, (const char *)inStr, kCFStringEncodingASCII); 209 if (!oidStr) goto cleanExit; 210 argvRef = CFStringCreateArrayBySeparatingStrings(NULL, oidStr, CFSTR(".")); 211 if (!argvRef) goto cleanExit; 212 argc = CFArrayGetCount(argvRef); 213 if (argc < 3) goto cleanExit; 214 215 /* first two numbers in OID munge together */ 216 num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 0)); 217 if (num < 0) goto cleanExit; 218 firstByte = (40 * num); 219 num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 1)); 220 if (num < 0) goto cleanExit; 221 firstByte += num; 222 numDigitBytes = 1; 223 224 numsToProcess = argc - 2; 225 if(numsToProcess > 0) { 226 /* skip this loop in the unlikely event that input is only two numbers */ 227 digits = (unsigned char **) malloc(numsToProcess * sizeof(unsigned char *)); 228 numDigits = (unsigned *) malloc(numsToProcess * sizeof(unsigned)); 229 for(digit=0; digit<numsToProcess; digit++) { 230 num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, digit+2)); 231 if (num < 0) goto cleanExit; 232 numDigits[digit] = encodeNumber(num, &digits[digit]); 233 numDigitBytes += numDigits[digit]; 234 } 235 } 236 *outLen = (2 + numDigitBytes); 237 *outOid = outP = (unsigned char *) malloc(*outLen); 238 *outP++ = 0x06; 239 *outP++ = numDigitBytes; 240 *outP++ = firstByte; 241 for(digit=0; digit<numsToProcess; digit++) { 242 unsigned int byteDex; 243 for(byteDex=0; byteDex<numDigits[digit]; byteDex++) { 244 *outP++ = digits[digit][byteDex]; 245 } 246 } 247 if(digits) { 248 for(digit=0; digit<numsToProcess; digit++) { 249 free(digits[digit]); 250 } 251 } 252 result = 0; 253 254 cleanExit: 255 if (digits) free(digits); 256 if (numDigits) free(numDigits); 257 if (oidStr) CFRelease(oidStr); 258 if (argvRef) CFRelease(argvRef); 259 260 return result; 261 } 262 263 /* 264 * Given a CF object reference describing an OID, convert to binary DER format 265 * and fill out the CSSM_OID structure provided by the caller. Caller is 266 * responsible for freeing the data pointer in outOid->Data. 267 * 268 * Function returns 0 if successful, non-zero otherwise. 269 */ 270 271 static int convertOid( 272 CFTypeRef inRef, 273 SecAsn1Oid *outOid) 274 { 275 if (!inRef || !outOid) 276 return errSecParam; 277 278 unsigned char *oidData = NULL; 279 unsigned int oidLen = 0; 280 281 if (CFGetTypeID(inRef) == CFStringGetTypeID()) { 282 // CFStringRef: OID representation is a dotted-decimal string 283 CFStringRef inStr = (CFStringRef)inRef; 284 CFIndex max = CFStringGetLength(inStr) * 3; 285 char *buf = (char *)malloc(max); 286 if (!buf) { 287 return errSecMemoryError; 288 } 289 if (!CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII)) { 290 free(buf); 291 return errSecParam; 292 } 293 294 if (encodeOid((unsigned char *)buf, &oidData, &oidLen) != 0) { 295 free(buf); 296 return errSecParam; 297 } 298 free(buf); 299 } 300 else if (CFGetTypeID(inRef) == CFDataGetTypeID()) { 301 // CFDataRef: OID representation is in binary DER format 302 CFDataRef inData = (CFDataRef)inRef; 303 oidLen = (unsigned int) CFDataGetLength(inData); 304 oidData = (unsigned char *) malloc(oidLen); 305 memcpy(oidData, CFDataGetBytePtr(inData), oidLen); 306 } 307 else { 308 // Not in a format we understand 309 return errSecParam; 310 } 311 outOid->Length = oidLen; 312 outOid->Data = (uint8_t *)oidData; 313 return 0; 314 } 315 316 static CFTypeID cmsEncoderTypeID = _kCFRuntimeNotATypeID; 317 318 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */ 319 static void cmsEncoderClassInitialize(void) 320 { 321 cmsEncoderTypeID = 322 _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsEncoderRuntimeClass); 323 } 324 325 /* init called out from _CFRuntimeCreateInstance() */ 326 static void cmsEncoderInit(CFTypeRef enc) 327 { 328 char *start = ((char *)enc) + sizeof(CFRuntimeBase); 329 memset(start, 0, sizeof(struct _CMSEncoder) - sizeof(CFRuntimeBase)); 330 } 331 332 /* 333 * Dispose of a CMSEncoder. Called out from CFRelease(). 334 */ 335 static void cmsEncoderFinalize( 336 CFTypeRef enc) 337 { 338 CMSEncoderRef cmsEncoder = (CMSEncoderRef)enc; 339 if(cmsEncoder == NULL) { 340 return; 341 } 342 if(cmsEncoder->eContentType.Data != NULL) { 343 free(cmsEncoder->eContentType.Data); 344 } 345 CFRELEASE(cmsEncoder->signers); 346 CFRELEASE(cmsEncoder->recipients); 347 CFRELEASE(cmsEncoder->otherCerts); 348 if(cmsEncoder->cmsMsg != NULL) { 349 SecCmsMessageDestroy(cmsEncoder->cmsMsg); 350 cmsEncoder->cmsMsg = NULL; 351 } 352 if(cmsEncoder->encoder != NULL) { 353 /* 354 * Normally this gets freed in SecCmsEncoderFinish - this is 355 * an error case. 356 */ 357 SecCmsEncoderDestroy(cmsEncoder->encoder); 358 } 359 } 360 361 static OSStatus cmsSetupEncoder( 362 CMSEncoderRef cmsEncoder) 363 { 364 OSStatus ortn; 365 366 ASSERT(cmsEncoder->cmsMsg != NULL); 367 ASSERT(cmsEncoder->encoder == NULL); 368 369 cmsEncoder->encoderOut = CFDataCreateMutable(NULL, 0); 370 if (!cmsEncoder->encoderOut) { 371 return errSecAllocate; 372 } 373 374 ortn = SecCmsEncoderCreate(cmsEncoder->cmsMsg, 375 NULL, NULL, // no callback 376 cmsEncoder->encoderOut, // data goes here 377 NULL, NULL, // no password callback (right?) 378 NULL, NULL, // decrypt key callback 379 &cmsEncoder->encoder); 380 if(ortn) { 381 return cmsRtnToOSStatus(ortn); 382 } 383 return errSecSuccess; 384 } 385 386 /* 387 * Set up a SecCmsMessageRef for a SignedData creation. 388 */ 389 static OSStatus cmsSetupForSignedData( 390 CMSEncoderRef cmsEncoder) 391 { 392 ASSERT((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL)); 393 394 SecCmsContentInfoRef contentInfo = NULL; 395 SecCmsSignedDataRef signedData = NULL; 396 OSStatus ortn; 397 398 /* build chain of objects: message->signedData->data */ 399 if(cmsEncoder->cmsMsg != NULL) { 400 SecCmsMessageDestroy(cmsEncoder->cmsMsg); 401 } 402 cmsEncoder->cmsMsg = SecCmsMessageCreate(); 403 if(cmsEncoder->cmsMsg == NULL) { 404 return errSecInternalComponent; 405 } 406 407 signedData = SecCmsSignedDataCreate(cmsEncoder->cmsMsg); 408 if(signedData == NULL) { 409 return errSecInternalComponent; 410 } 411 contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg); 412 ortn = SecCmsContentInfoSetContentSignedData(contentInfo,signedData); 413 if(ortn) { 414 return cmsRtnToOSStatus(ortn); 415 } 416 contentInfo = SecCmsSignedDataGetContentInfo(signedData); 417 if(cmsEncoder->eContentType.Data != NULL) { 418 /* Override the default eContentType of id-data */ 419 ortn = SecCmsContentInfoSetContentOther(contentInfo, 420 NULL, /* data - provided to encoder, not here */ 421 cmsEncoder->detachedContent, 422 &cmsEncoder->eContentType); 423 } 424 else { 425 ortn = SecCmsContentInfoSetContentData(contentInfo, 426 NULL, /* data - provided to encoder, not here */ 427 cmsEncoder->detachedContent); 428 } 429 if(ortn) { 430 ortn = cmsRtnToOSStatus(ortn); 431 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn); 432 return ortn; 433 } 434 435 /* optional 'global' (per-SignedData) certs */ 436 if(cmsEncoder->otherCerts != NULL) { 437 ortn = SecCmsSignedDataAddCertList(signedData, cmsEncoder->otherCerts); 438 if(ortn) { 439 ortn = cmsRtnToOSStatus(ortn); 440 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn); 441 return ortn; 442 } 443 } 444 445 /* SignerInfos, one per signer */ 446 CFIndex numSigners = 0; 447 if(cmsEncoder->signers != NULL) { 448 /* this is optional...in case we're just creating a cert bundle */ 449 numSigners = CFArrayGetCount(cmsEncoder->signers); 450 } 451 CFIndex dex; 452 SecCertificateRef ourCert = NULL; 453 SecCmsCertChainMode chainMode = SecCmsCMCertChain; 454 455 switch(cmsEncoder->chainMode) { 456 case kCMSCertificateNone: 457 chainMode = SecCmsCMNone; 458 break; 459 case kCMSCertificateSignerOnly: 460 chainMode = SecCmsCMCertOnly; 461 break; 462 case kCMSCertificateChainWithRoot: 463 chainMode = SecCmsCMCertChainWithRoot; 464 break; 465 case kCMSCertificateChainWithRootOrFail: 466 chainMode = SecCmsCMCertChainWithRootOrFail; 467 break; 468 default: 469 break; 470 } 471 for(dex=0; dex<numSigners; dex++) { 472 SecCmsSignerInfoRef signerInfo; 473 474 SecIdentityRef ourId = 475 (SecIdentityRef)CFArrayGetValueAtIndex(cmsEncoder->signers, dex); 476 ortn = SecIdentityCopyCertificate(ourId, &ourCert); 477 if(ortn) { 478 CSSM_PERROR("SecIdentityCopyCertificate", ortn); 479 break; 480 } 481 /* this creates the signerInfo and adds it to the signedData object */ 482 signerInfo = SecCmsSignerInfoCreate(signedData, ourId, cmsEncoder->digestalgtag); 483 if (signerInfo == NULL) { 484 ortn = errSecInternalComponent; 485 break; 486 } 487 488 /* we want the cert chain included for this one */ 489 /* NOTE the usage parameter is currently unused by the SMIME lib */ 490 ortn = SecCmsSignerInfoIncludeCerts(signerInfo, chainMode, 491 certUsageEmailSigner); 492 if(ortn) { 493 ortn = cmsRtnToOSStatus(ortn); 494 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn); 495 break; 496 } 497 498 /* other options */ 499 if(cmsEncoder->signedAttributes & kCMSAttrSmimeCapabilities) { 500 ortn = SecCmsSignerInfoAddSMIMECaps(signerInfo); 501 if(ortn) { 502 ortn = cmsRtnToOSStatus(ortn); 503 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn); 504 break; 505 } 506 } 507 if(cmsEncoder->signedAttributes & kCMSAttrSmimeEncryptionKeyPrefs) { 508 ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, NULL); 509 if(ortn) { 510 ortn = cmsRtnToOSStatus(ortn); 511 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn); 512 break; 513 } 514 } 515 if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) { 516 ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, NULL); 517 if(ortn) { 518 ortn = cmsRtnToOSStatus(ortn); 519 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn); 520 break; 521 } 522 } 523 if(cmsEncoder->signedAttributes & kCMSAttrSigningTime) { 524 if (cmsEncoder->signingTime == 0) 525 cmsEncoder->signingTime = CFAbsoluteTimeGetCurrent(); 526 ortn = SecCmsSignerInfoAddSigningTime(signerInfo, cmsEncoder->signingTime); 527 if(ortn) { 528 ortn = cmsRtnToOSStatus(ortn); 529 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn); 530 break; 531 } 532 } 533 if(cmsEncoder->signedAttributes & kCMSAttrAppleCodesigningHashAgility) { 534 ortn = SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo, cmsEncoder->hashAgilityAttrValue); 535 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */ 536 CFReleaseNull(cmsEncoder->hashAgilityAttrValue); 537 if(ortn) { 538 ortn = cmsRtnToOSStatus(ortn); 539 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn); 540 break; 541 } 542 } 543 if(cmsEncoder->signedAttributes & kCMSAttrAppleCodesigningHashAgilityV2) { 544 ortn = SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(signerInfo, cmsEncoder->hashAgilityV2AttrValues); 545 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */ 546 CFReleaseNull(cmsEncoder->hashAgilityV2AttrValues); 547 if(ortn) { 548 ortn = cmsRtnToOSStatus(ortn); 549 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgilityV2", ortn); 550 break; 551 } 552 } 553 if (cmsEncoder->signedAttributes & kCMSAttrAppleExpirationTime) { 554 ortn = SecCmsSignerInfoAddAppleExpirationTime(signerInfo, cmsEncoder->expirationTime); 555 if(ortn) { 556 ortn = cmsRtnToOSStatus(ortn); 557 CSSM_PERROR("SecCmsSignerInfoAddAppleExpirationTime", ortn); 558 break; 559 } 560 } 561 562 CFRELEASE(ourCert); 563 ourCert = NULL; 564 } 565 if(ortn) { 566 CFRELEASE(ourCert); 567 } 568 return ortn; 569 } 570 571 /* 572 * Set up a SecCmsMessageRef for a EnvelopedData creation. 573 */ 574 static OSStatus cmsSetupForEnvelopedData( 575 CMSEncoderRef cmsEncoder) 576 { 577 ASSERT(cmsEncoder->op == EO_Encrypt); 578 ASSERT(cmsEncoder->recipients != NULL); 579 580 SecCmsContentInfoRef contentInfo = NULL; 581 SecCmsEnvelopedDataRef envelopedData = NULL; 582 SECOidTag algorithmTag; 583 int keySize; 584 OSStatus ortn; 585 586 /* 587 * Find encryption algorithm...unfortunately we need a NULL-terminated array 588 * of SecCertificateRefs for this. 589 */ 590 CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients); 591 CFIndex dex; 592 SecCertificateRef *certArray = (SecCertificateRef *)malloc( 593 (numCerts+1) * sizeof(SecCertificateRef)); 594 595 for(dex=0; dex<numCerts; dex++) { 596 certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex( 597 cmsEncoder->recipients, dex); 598 } 599 certArray[numCerts] = NULL; 600 ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize); 601 free(certArray); 602 if(ortn) { 603 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn); 604 return ortn; 605 } 606 607 /* build chain of objects: message->envelopedData->data */ 608 if(cmsEncoder->cmsMsg != NULL) { 609 SecCmsMessageDestroy(cmsEncoder->cmsMsg); 610 } 611 cmsEncoder->cmsMsg = SecCmsMessageCreate(); 612 if(cmsEncoder->cmsMsg == NULL) { 613 return errSecInternalComponent; 614 } 615 envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg, 616 algorithmTag, keySize); 617 if(envelopedData == NULL) { 618 return errSecInternalComponent; 619 } 620 contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg); 621 ortn = SecCmsContentInfoSetContentEnvelopedData(contentInfo, envelopedData); 622 if(ortn) { 623 ortn = cmsRtnToOSStatus(ortn); 624 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn); 625 return ortn; 626 } 627 contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData); 628 if(cmsEncoder->eContentType.Data != NULL) { 629 /* Override the default ContentType of id-data */ 630 ortn = SecCmsContentInfoSetContentOther(contentInfo, 631 NULL, /* data - provided to encoder, not here */ 632 FALSE, /* detachedContent */ 633 &cmsEncoder->eContentType); 634 } 635 else { 636 ortn = SecCmsContentInfoSetContentData(contentInfo, 637 NULL /* data - provided to encoder, not here */, 638 cmsEncoder->detachedContent); 639 } 640 if(ortn) { 641 ortn = cmsRtnToOSStatus(ortn); 642 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn); 643 return ortn; 644 } 645 646 /* 647 * create & attach recipient information, one for each recipient 648 */ 649 for(dex=0; dex<numCerts; dex++) { 650 SecCmsRecipientInfoRef recipientInfo = NULL; 651 652 SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex( 653 cmsEncoder->recipients, dex); 654 recipientInfo = SecCmsRecipientInfoCreate(envelopedData, thisRecip); 655 ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo); 656 if(ortn) { 657 ortn = cmsRtnToOSStatus(ortn); 658 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn); 659 return ortn; 660 } 661 } 662 return errSecSuccess; 663 } 664 665 /* 666 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or 667 * from CMSEncodeGetCmsMessage(). 668 */ 669 static OSStatus cmsSetupCmsMsg( 670 CMSEncoderRef cmsEncoder) 671 { 672 ASSERT(cmsEncoder != NULL); 673 ASSERT(cmsEncoder->encState == ES_Init); 674 675 /* figure out what high-level operation we're doing */ 676 if((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL)) { 677 if(cmsEncoder->recipients != NULL) { 678 cmsEncoder->op = EO_SignEncrypt; 679 } 680 else { 681 cmsEncoder->op = EO_Sign; 682 } 683 } 684 else if(cmsEncoder->recipients != NULL) { 685 cmsEncoder->op = EO_Encrypt; 686 } 687 else { 688 dprintf("CMSEncoderUpdateContent: nothing to do\n"); 689 return errSecParam; 690 } 691 692 OSStatus ortn = errSecSuccess; 693 694 switch(cmsEncoder->op) { 695 case EO_Sign: 696 case EO_SignEncrypt: 697 /* If we're signing & encrypting, do the signing first */ 698 ortn = cmsSetupForSignedData(cmsEncoder); 699 break; 700 case EO_Encrypt: 701 ortn = cmsSetupForEnvelopedData(cmsEncoder); 702 break; 703 } 704 cmsEncoder->encState = ES_Msg; 705 return ortn; 706 } 707 708 /* 709 * ASN.1 template for decoding a ContentInfo. 710 */ 711 typedef struct { 712 SecAsn1Oid contentType; 713 SecAsn1Oid content; 714 } SimpleContentInfo; 715 716 static const SecAsn1Template cmsSimpleContentInfoTemplate[] = { 717 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) }, 718 { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) }, 719 { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, 720 offsetof(SimpleContentInfo, content), 721 kSecAsn1AnyTemplate }, 722 { 0, } 723 }; 724 725 /* 726 * Obtain the content of a contentInfo, This basically strips off the contentType OID 727 * and returns its ASN_ANY content, allocated the provided coder's memory space. 728 */ 729 static OSStatus cmsContentInfoContent( 730 SecAsn1CoderRef asn1Coder, 731 const SecAsn1Item *contentInfo, 732 SecAsn1Item *content) /* RETURNED */ 733 { 734 OSStatus ortn; 735 SimpleContentInfo decodedInfo; 736 737 memset(&decodedInfo, 0, sizeof(decodedInfo)); 738 ortn = SecAsn1DecodeData(asn1Coder, contentInfo, 739 cmsSimpleContentInfoTemplate, &decodedInfo); 740 if(ortn) { 741 return ortn; 742 } 743 if(decodedInfo.content.Data == NULL) { 744 dprintf("***Error decoding contentInfo: no content\n"); 745 return errSecInternalComponent; 746 } 747 *content = decodedInfo.content; 748 return errSecSuccess; 749 } 750 751 #pragma mark --- Start of Public API --- 752 753 CFTypeID CMSEncoderGetTypeID(void) 754 { 755 static pthread_once_t once = PTHREAD_ONCE_INIT; 756 757 if(cmsEncoderTypeID == _kCFRuntimeNotATypeID) { 758 pthread_once(&once, &cmsEncoderClassInitialize); 759 } 760 return cmsEncoderTypeID; 761 } 762 763 /* 764 * Create a CMSEncoder. Result must eventually be freed via CFRelease(). 765 */ 766 OSStatus CMSEncoderCreate( 767 CMSEncoderRef *cmsEncoderOut) /* RETURNED */ 768 { 769 CMSEncoderRef cmsEncoder = NULL; 770 771 uint32_t extra = sizeof(*cmsEncoder) - sizeof(cmsEncoder->base); 772 cmsEncoder = (CMSEncoderRef)_CFRuntimeCreateInstance(NULL, CMSEncoderGetTypeID(), 773 extra, NULL); 774 if(cmsEncoder == NULL) { 775 return errSecAllocate; 776 } 777 cmsEncoder->encState = ES_Init; 778 cmsEncoder->chainMode = kCMSCertificateChain; 779 cmsEncoder->digestalgtag = SEC_OID_SHA1; 780 *cmsEncoderOut = cmsEncoder; 781 return errSecSuccess; 782 } 783 784 #pragma mark --- Getters & Setters --- 785 786 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1 = CFSTR("sha1"); 787 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256 = CFSTR("sha256"); 788 789 790 OSStatus CMSEncoderSetSignerAlgorithm( 791 CMSEncoderRef cmsEncoder, 792 CFStringRef digestAlgorithm) 793 { 794 if (CFEqual(digestAlgorithm, kCMSEncoderDigestAlgorithmSHA1)) { 795 cmsEncoder->digestalgtag = SEC_OID_SHA1; 796 } else if (CFEqual(digestAlgorithm, kCMSEncoderDigestAlgorithmSHA256)) { 797 cmsEncoder->digestalgtag = SEC_OID_SHA256; 798 } else { 799 return errSecParam; 800 } 801 802 return errSecSuccess; 803 } 804 805 /* 806 * Specify signers of the CMS message; implies that the message will be signed. 807 */ 808 OSStatus CMSEncoderAddSigners( 809 CMSEncoderRef cmsEncoder, 810 CFTypeRef signerOrArray) 811 { 812 if(cmsEncoder == NULL) { 813 return errSecParam; 814 } 815 if(cmsEncoder->encState != ES_Init) { 816 return errSecParam; 817 } 818 return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID()); 819 } 820 821 /* 822 * Obtain an array of signers as specified in CMSEncoderSetSigners(). 823 */ 824 OSStatus CMSEncoderCopySigners( 825 CMSEncoderRef cmsEncoder, 826 CFArrayRef *signers) 827 { 828 if((cmsEncoder == NULL) || (signers == NULL)) { 829 return errSecParam; 830 } 831 if(cmsEncoder->signers != NULL) { 832 CFRetain(cmsEncoder->signers); 833 } 834 *signers = cmsEncoder->signers; 835 return errSecSuccess; 836 } 837 838 /* 839 * Specify recipients of the message. Implies that the message will be encrypted. 840 */ 841 OSStatus CMSEncoderAddRecipients( 842 CMSEncoderRef cmsEncoder, 843 CFTypeRef recipientOrArray) 844 { 845 if(cmsEncoder == NULL) { 846 return errSecParam; 847 } 848 if(cmsEncoder->encState != ES_Init) { 849 return errSecParam; 850 } 851 return cmsAppendToArray(recipientOrArray, &cmsEncoder->recipients, 852 SecCertificateGetTypeID()); 853 } 854 855 /* 856 * Obtain an array of recipients as specified in CMSEncoderSetRecipients(). 857 */ 858 OSStatus CMSEncoderCopyRecipients( 859 CMSEncoderRef cmsEncoder, 860 CFArrayRef *recipients) 861 { 862 if((cmsEncoder == NULL) || (recipients == NULL)) { 863 return errSecParam; 864 } 865 if(cmsEncoder->recipients != NULL) { 866 CFRetain(cmsEncoder->recipients); 867 } 868 *recipients = cmsEncoder->recipients; 869 return errSecSuccess; 870 } 871 872 /* 873 * Specify additional certs to include in a signed message. 874 */ 875 OSStatus CMSEncoderAddSupportingCerts( 876 CMSEncoderRef cmsEncoder, 877 CFTypeRef certOrArray) 878 { 879 if(cmsEncoder == NULL) { 880 return errSecParam; 881 } 882 if(cmsEncoder->encState != ES_Init) { 883 return errSecParam; 884 } 885 return cmsAppendToArray(certOrArray, &cmsEncoder->otherCerts, 886 SecCertificateGetTypeID()); 887 } 888 889 /* 890 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts(). 891 */ 892 OSStatus CMSEncoderCopySupportingCerts( 893 CMSEncoderRef cmsEncoder, 894 CFArrayRef *certs) /* RETURNED */ 895 { 896 if((cmsEncoder == NULL) || (certs == NULL)) { 897 return errSecParam; 898 } 899 if(cmsEncoder->otherCerts != NULL) { 900 CFRetain(cmsEncoder->otherCerts); 901 } 902 *certs = cmsEncoder->otherCerts; 903 return errSecSuccess; 904 } 905 906 OSStatus CMSEncoderSetHasDetachedContent( 907 CMSEncoderRef cmsEncoder, 908 Boolean detachedContent) 909 { 910 if(cmsEncoder == NULL) { 911 return errSecParam; 912 } 913 if(cmsEncoder->encState != ES_Init) { 914 return errSecParam; 915 } 916 cmsEncoder->detachedContent = detachedContent; 917 return errSecSuccess; 918 } 919 920 OSStatus CMSEncoderGetHasDetachedContent( 921 CMSEncoderRef cmsEncoder, 922 Boolean *detachedContent) /* RETURNED */ 923 { 924 if((cmsEncoder == NULL) || (detachedContent == NULL)) { 925 return errSecParam; 926 } 927 *detachedContent = cmsEncoder->detachedContent; 928 return errSecSuccess; 929 } 930 931 /* 932 * Optionally specify an eContentType OID for the inner EncapsulatedData for 933 * a signed message. The default eContentType, used of this function is not 934 * called, is id-data. 935 */ 936 static OSStatus CMSEncoderSetEncapsulatedContentType( 937 CMSEncoderRef cmsEncoder, 938 const SecAsn1Oid *eContentType) 939 { 940 if((cmsEncoder == NULL) || (eContentType == NULL)) { 941 return errSecParam; 942 } 943 if(cmsEncoder->encState != ES_Init) { 944 return errSecParam; 945 } 946 947 SecAsn1Oid *ecOid = &cmsEncoder->eContentType; 948 if(ecOid->Data != NULL) { 949 free(ecOid->Data); 950 } 951 cmsCopyCmsData(eContentType, ecOid); 952 return errSecSuccess; 953 } 954 955 OSStatus CMSEncoderSetEncapsulatedContentTypeOID( 956 CMSEncoderRef cmsEncoder, 957 CFTypeRef eContentTypeOID) 958 { 959 // convert eContentTypeOID to a CSSM_OID 960 SecAsn1Oid contentType = { 0, NULL }; 961 if (!eContentTypeOID || convertOid(eContentTypeOID, &contentType) != 0) 962 return errSecParam; 963 OSStatus result = CMSEncoderSetEncapsulatedContentType(cmsEncoder, &contentType); 964 if (contentType.Data) 965 free(contentType.Data); 966 return result; 967 } 968 969 /* 970 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType(). 971 */ 972 OSStatus CMSEncoderCopyEncapsulatedContentType( 973 CMSEncoderRef cmsEncoder, 974 CFDataRef *eContentType) 975 { 976 if((cmsEncoder == NULL) || (eContentType == NULL)) { 977 return errSecParam; 978 } 979 980 SecAsn1Oid *ecOid = &cmsEncoder->eContentType; 981 if(ecOid->Data == NULL) { 982 *eContentType = NULL; 983 } 984 else { 985 *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length); 986 } 987 return errSecSuccess; 988 } 989 990 /* 991 * Optionally specify signed attributes. Only meaningful when creating a 992 * signed message. If this is called, it must be called before 993 * CMSEncoderUpdateContent(). 994 */ 995 OSStatus CMSEncoderAddSignedAttributes( 996 CMSEncoderRef cmsEncoder, 997 CMSSignedAttributes signedAttributes) 998 { 999 if(cmsEncoder == NULL) { 1000 return errSecParam; 1001 } 1002 if(cmsEncoder->encState != ES_Init) { 1003 return errSecParam; 1004 } 1005 cmsEncoder->signedAttributes |= signedAttributes; 1006 return errSecSuccess; 1007 } 1008 1009 /* 1010 * Set the signing time for a CMSEncoder. 1011 * This is only used if the kCMSAttrSigningTime attribute is included. 1012 */ 1013 OSStatus CMSEncoderSetSigningTime( 1014 CMSEncoderRef cmsEncoder, 1015 CFAbsoluteTime time) 1016 { 1017 if(cmsEncoder == NULL) { 1018 return errSecParam; 1019 } 1020 if(cmsEncoder->encState != ES_Init) { 1021 return errSecParam; 1022 } 1023 cmsEncoder->signingTime = time; 1024 return errSecSuccess; 1025 } 1026 1027 /* 1028 * Set the hash agility attribute for a CMSEncoder. 1029 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute 1030 * is included. 1031 */ 1032 OSStatus CMSEncoderSetAppleCodesigningHashAgility( 1033 CMSEncoderRef cmsEncoder, 1034 CFDataRef hashAgilityAttrValue) 1035 { 1036 if (cmsEncoder == NULL || cmsEncoder->encState != ES_Init) { 1037 return errSecParam; 1038 } 1039 cmsEncoder->hashAgilityAttrValue = CFRetainSafe(hashAgilityAttrValue); 1040 return errSecSuccess; 1041 } 1042 1043 /* 1044 * Set the hash agility attribute for a CMSEncoder. 1045 * This is only used if the kCMSAttrAppleCodesigningHashAgilityV2 attribute 1046 * is included. 1047 */ 1048 OSStatus CMSEncoderSetAppleCodesigningHashAgilityV2( 1049 CMSEncoderRef cmsEncoder, 1050 CFDictionaryRef hashAgilityV2AttrValues) 1051 { 1052 if (cmsEncoder == NULL || cmsEncoder->encState != ES_Init) { 1053 return errSecParam; 1054 } 1055 cmsEncoder->hashAgilityV2AttrValues = CFRetainSafe(hashAgilityV2AttrValues); 1056 return errSecSuccess; 1057 } 1058 1059 /* 1060 * Set the expiration time for a CMSEncoder. 1061 * This is only used if the kCMSAttrAppleExpirationTime attribute is included. 1062 */ 1063 OSStatus CMSEncoderSetAppleExpirationTime( 1064 CMSEncoderRef cmsEncoder, 1065 CFAbsoluteTime time) 1066 { 1067 if(cmsEncoder == NULL) { 1068 return errSecParam; 1069 } 1070 if(cmsEncoder->encState != ES_Init) { 1071 return errSecParam; 1072 } 1073 cmsEncoder->expirationTime = time; 1074 return errSecSuccess; 1075 } 1076 1077 OSStatus CMSEncoderSetCertificateChainMode( 1078 CMSEncoderRef cmsEncoder, 1079 CMSCertificateChainMode chainMode) 1080 { 1081 if(cmsEncoder == NULL) { 1082 return errSecParam; 1083 } 1084 if(cmsEncoder->encState != ES_Init) { 1085 return errSecParam; 1086 } 1087 switch(chainMode) { 1088 case kCMSCertificateNone: 1089 case kCMSCertificateSignerOnly: 1090 case kCMSCertificateChain: 1091 case kCMSCertificateChainWithRoot: 1092 case kCMSCertificateChainWithRootOrFail: 1093 break; 1094 default: 1095 return errSecParam; 1096 } 1097 cmsEncoder->chainMode = chainMode; 1098 return errSecSuccess; 1099 } 1100 1101 OSStatus CMSEncoderGetCertificateChainMode( 1102 CMSEncoderRef cmsEncoder, 1103 CMSCertificateChainMode *chainModeOut) 1104 { 1105 if(cmsEncoder == NULL) { 1106 return errSecParam; 1107 } 1108 *chainModeOut = cmsEncoder->chainMode; 1109 return errSecSuccess; 1110 } 1111 1112 #if TIMESTAMPING_SUPPORTED 1113 void 1114 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback) 1115 { 1116 if (cmsEncoder->cmsMsg) 1117 SecCmsMessageSetTSACallback(cmsEncoder->cmsMsg, tsaCallback); 1118 } 1119 1120 void 1121 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder, CFTypeRef tsaContext) 1122 { 1123 if (cmsEncoder->cmsMsg) 1124 SecCmsMessageSetTSAContext(cmsEncoder->cmsMsg, tsaContext); 1125 } 1126 #endif 1127 1128 #pragma mark --- Action --- 1129 1130 /* 1131 * Feed content bytes into the encoder. 1132 * Can be called multiple times. 1133 * No 'setter' routines can be called after this function has been called. 1134 */ 1135 OSStatus CMSEncoderUpdateContent( 1136 CMSEncoderRef cmsEncoder, 1137 const void *content, 1138 size_t contentLen) 1139 { 1140 if(cmsEncoder == NULL) { 1141 return errSecParam; 1142 } 1143 1144 OSStatus ortn = errSecSuccess; 1145 switch(cmsEncoder->encState) { 1146 case ES_Init: 1147 /* 1148 * First time thru: do the CmsMsg setup. 1149 */ 1150 ortn = cmsSetupCmsMsg(cmsEncoder); 1151 if(ortn) { 1152 return ortn; 1153 } 1154 /* fall thru to set up the encoder */ 1155 1156 case ES_Msg: 1157 /* We have a cmsMsg but no encoder; create one */ 1158 ASSERT(cmsEncoder->cmsMsg != NULL); 1159 ASSERT(cmsEncoder->encoder == NULL); 1160 ortn = cmsSetupEncoder(cmsEncoder); 1161 if(ortn) { 1162 return ortn; 1163 } 1164 /* only legal calls now are update and finalize */ 1165 cmsEncoder->encState = ES_Updating; 1166 break; 1167 1168 case ES_Updating: 1169 ASSERT(cmsEncoder->encoder != NULL); 1170 break; 1171 1172 case ES_Final: 1173 /* Too late for another update */ 1174 return errSecParam; 1175 1176 default: 1177 return errSecInternalComponent; 1178 } 1179 1180 /* FIXME - CFIndex same size as size_t on 64bit? */ 1181 ortn = SecCmsEncoderUpdate(cmsEncoder->encoder, content, (CFIndex)contentLen); 1182 if(ortn) { 1183 ortn = cmsRtnToOSStatus(ortn); 1184 CSSM_PERROR("SecCmsEncoderUpdate", ortn); 1185 } 1186 return ortn; 1187 } 1188 1189 /* forward declaration */ 1190 static OSStatus CMSEncode( 1191 CFTypeRef signers, 1192 CFTypeRef recipients, 1193 const SecAsn1Oid *eContentType, 1194 Boolean detachedContent, 1195 CMSSignedAttributes signedAttributes, 1196 const void *content, 1197 size_t contentLen, 1198 CFDataRef *encodedContent); 1199 1200 /* 1201 * Finish encoding the message and obtain the encoded result. 1202 * Caller must CFRelease the result. 1203 */ 1204 OSStatus CMSEncoderCopyEncodedContent( 1205 CMSEncoderRef cmsEncoder, 1206 CFDataRef *encodedContent) 1207 { 1208 if((cmsEncoder == NULL) || (encodedContent == NULL)) { 1209 return errSecParam; 1210 } 1211 1212 OSStatus ortn; 1213 1214 switch(cmsEncoder->encState) { 1215 case ES_Updating: 1216 /* normal termination */ 1217 break; 1218 case ES_Final: 1219 /* already been called */ 1220 return errSecParam; 1221 case ES_Msg: 1222 case ES_Init: 1223 /* 1224 * The only time these are legal is when we're doing a SignedData 1225 * with certificates only (no signers, no content). 1226 */ 1227 if((cmsEncoder->signers != NULL) || 1228 (cmsEncoder->recipients != NULL) || 1229 (cmsEncoder->otherCerts == NULL)) { 1230 return errSecParam; 1231 } 1232 1233 /* Set up for certs only */ 1234 ortn = cmsSetupForSignedData(cmsEncoder); 1235 if(ortn) { 1236 return ortn; 1237 } 1238 /* and an encoder */ 1239 ortn = cmsSetupEncoder(cmsEncoder); 1240 if(ortn) { 1241 return ortn; 1242 } 1243 break; 1244 } 1245 1246 1247 ASSERT(cmsEncoder->encoder != NULL); 1248 ortn = SecCmsEncoderFinish(cmsEncoder->encoder); 1249 /* regardless of the outcome, the encoder itself has been freed */ 1250 cmsEncoder->encoder = NULL; 1251 if(ortn) { 1252 return cmsRtnToOSStatus(ortn); 1253 } 1254 cmsEncoder->encState = ES_Final; 1255 1256 if((cmsEncoder->encoderOut == NULL) && !cmsEncoder->customCoder) { 1257 /* not sure how this could happen... */ 1258 dprintf("Successful encode, but no data\n"); 1259 return errSecInternalComponent; 1260 } 1261 if(cmsEncoder->customCoder) { 1262 /* we're done */ 1263 *encodedContent = NULL; 1264 return errSecSuccess; 1265 } 1266 1267 /* in two out of three cases, we're done */ 1268 switch(cmsEncoder->op) { 1269 case EO_Sign: 1270 case EO_Encrypt: 1271 *encodedContent = CFDataCreateCopy(NULL, cmsEncoder->encoderOut); 1272 return errSecSuccess; 1273 case EO_SignEncrypt: 1274 /* proceed, more work to do */ 1275 break; 1276 } 1277 1278 /* 1279 * Signing & encrypting. 1280 * Due to bugs in the libsecurity_smime encoder, it can't encode nested 1281 * ContentInfos in one shot. So we do another pass, specifying the SignedData 1282 * inside of the ContentInfo we just created as the data to encrypt. 1283 */ 1284 SecAsn1CoderRef asn1Coder = NULL; 1285 SecAsn1Item signedData = {0, NULL}; 1286 1287 ortn = SecAsn1CoderCreate(&asn1Coder); 1288 if(ortn) { 1289 return ortn; 1290 } 1291 SecAsn1Item encoderOut = { CFDataGetLength(cmsEncoder->encoderOut), 1292 CFDataGetMutableBytePtr(cmsEncoder->encoderOut)}; 1293 ortn = cmsContentInfoContent(asn1Coder, &encoderOut, &signedData); 1294 if(ortn) { 1295 goto errOut; 1296 } 1297 1298 /* now just encrypt that, one-shot */ 1299 ortn = CMSEncode(NULL, /* no signers this time */ 1300 cmsEncoder->recipients, 1301 &CSSMOID_PKCS7_SignedData, /* fake out encoder so it doesn't try to actually 1302 * encode the signedData - this asserts the 1303 * SEC_OID_OTHER OID tag in the EnvelopedData's 1304 * ContentInfo */ 1305 FALSE, /* detachedContent */ 1306 kCMSAttrNone, /* signedAttributes - none this time */ 1307 signedData.Data, signedData.Length, 1308 encodedContent); 1309 1310 errOut: 1311 if(asn1Coder) { 1312 SecAsn1CoderRelease(asn1Coder); 1313 } 1314 return ortn; 1315 } 1316 1317 #pragma mark --- High-level API --- 1318 1319 /* 1320 * High-level, one-shot encoder function. 1321 */ 1322 static OSStatus CMSEncode( 1323 CFTypeRef signers, 1324 CFTypeRef recipients, 1325 const SecAsn1Oid *eContentType, 1326 Boolean detachedContent, 1327 CMSSignedAttributes signedAttributes, 1328 const void *content, 1329 size_t contentLen, 1330 CFDataRef *encodedContent) /* RETURNED */ 1331 { 1332 if((signers == NULL) && (recipients == NULL)) { 1333 return errSecParam; 1334 } 1335 if(encodedContent == NULL) { 1336 return errSecParam; 1337 } 1338 1339 CMSEncoderRef cmsEncoder; 1340 OSStatus ortn; 1341 1342 /* set up the encoder */ 1343 ortn = CMSEncoderCreate(&cmsEncoder); 1344 if(ortn) { 1345 return ortn; 1346 } 1347 1348 /* subsequent errors to errOut: */ 1349 if(signers) { 1350 ortn = CMSEncoderAddSigners(cmsEncoder, signers); 1351 if(ortn) { 1352 goto errOut; 1353 } 1354 } 1355 if(recipients) { 1356 ortn = CMSEncoderAddRecipients(cmsEncoder, recipients); 1357 if(ortn) { 1358 goto errOut; 1359 } 1360 } 1361 if(eContentType) { 1362 ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType); 1363 if(ortn) { 1364 goto errOut; 1365 } 1366 } 1367 if(detachedContent) { 1368 ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent); 1369 if(ortn) { 1370 goto errOut; 1371 } 1372 } 1373 if(signedAttributes) { 1374 ortn = CMSEncoderAddSignedAttributes(cmsEncoder, signedAttributes); 1375 if(ortn) { 1376 goto errOut; 1377 } 1378 } 1379 /* GO */ 1380 ortn = CMSEncoderUpdateContent(cmsEncoder, content, contentLen); 1381 if(ortn) { 1382 goto errOut; 1383 } 1384 ortn = CMSEncoderCopyEncodedContent(cmsEncoder, encodedContent); 1385 1386 errOut: 1387 CFRelease(cmsEncoder); 1388 return ortn; 1389 } 1390 1391 OSStatus CMSEncodeContent( 1392 CFTypeRef signers, 1393 CFTypeRef recipients, 1394 CFTypeRef eContentTypeOID, 1395 Boolean detachedContent, 1396 CMSSignedAttributes signedAttributes, 1397 const void *content, 1398 size_t contentLen, 1399 CFDataRef *encodedContentOut) /* RETURNED */ 1400 { 1401 // convert eContentTypeOID to a CSSM_OID 1402 SecAsn1Oid contentType = { 0, NULL }; 1403 if (eContentTypeOID && convertOid(eContentTypeOID, &contentType) != 0) 1404 return errSecParam; 1405 const SecAsn1Oid *contentTypePtr = (eContentTypeOID) ? &contentType : NULL; 1406 OSStatus result = CMSEncode(signers, recipients, contentTypePtr, 1407 detachedContent, signedAttributes, 1408 content, contentLen, encodedContentOut); 1409 if (contentType.Data) 1410 free(contentType.Data); 1411 return result; 1412 } 1413 1414 #pragma mark --- SPI routines declared in CMSPrivate.h --- 1415 1416 /* 1417 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef. 1418 * If we don't have a SecCmsMessageRef yet, we create one now. 1419 * This is the only place where we go to state ES_Msg. 1420 */ 1421 OSStatus CMSEncoderGetCmsMessage( 1422 CMSEncoderRef cmsEncoder, 1423 SecCmsMessageRef *cmsMessage) /* RETURNED */ 1424 { 1425 if((cmsEncoder == NULL) || (cmsMessage == NULL)) { 1426 return errSecParam; 1427 } 1428 if(cmsEncoder->cmsMsg != NULL) { 1429 ASSERT(cmsEncoder->encState != ES_Init); 1430 *cmsMessage = cmsEncoder->cmsMsg; 1431 return errSecSuccess; 1432 } 1433 1434 OSStatus ortn = cmsSetupCmsMsg(cmsEncoder); 1435 if(ortn) { 1436 return ortn; 1437 } 1438 *cmsMessage = cmsEncoder->cmsMsg; 1439 1440 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */ 1441 cmsEncoder->encState = ES_Msg; 1442 return errSecSuccess; 1443 } 1444 1445 /* 1446 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef. 1447 * If this is called, it must be called before the first call to 1448 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the 1449 * incoming SecCmsEncoderRef. 1450 */ 1451 OSStatus CMSEncoderSetEncoder( 1452 CMSEncoderRef cmsEncoder, 1453 SecCmsEncoderRef encoder) 1454 { 1455 if((cmsEncoder == NULL) || (encoder == NULL)) { 1456 return errSecParam; 1457 } 1458 1459 OSStatus ortn; 1460 1461 switch(cmsEncoder->encState) { 1462 case ES_Init: 1463 /* No message, no encoder */ 1464 ASSERT(cmsEncoder->cmsMsg == NULL); 1465 ASSERT(cmsEncoder->encoder == NULL); 1466 ortn = cmsSetupCmsMsg(cmsEncoder); 1467 if(ortn) { 1468 return ortn; 1469 } 1470 /* drop thru to set encoder */ 1471 case ES_Msg: 1472 /* cmsMsg but no encoder */ 1473 ASSERT(cmsEncoder->cmsMsg != NULL); 1474 ASSERT(cmsEncoder->encoder == NULL); 1475 cmsEncoder->encoder = encoder; 1476 cmsEncoder->encState = ES_Updating; 1477 cmsEncoder->customCoder = true; /* we won't see data */ 1478 return errSecSuccess; 1479 default: 1480 /* no can do, too late */ 1481 return errSecParam; 1482 } 1483 } 1484 1485 /* 1486 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef. 1487 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor 1488 * CMSEncoderUpdateContent() has been called. 1489 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef. 1490 */ 1491 OSStatus CMSEncoderGetEncoder( 1492 CMSEncoderRef cmsEncoder, 1493 SecCmsEncoderRef *encoder) /* RETURNED */ 1494 { 1495 if((cmsEncoder == NULL) || (encoder == NULL)) { 1496 return errSecParam; 1497 } 1498 1499 /* any state, whether we have an encoder or not is OK */ 1500 *encoder = cmsEncoder->encoder; 1501 return errSecSuccess; 1502 } 1503 1504 #if TIMESTAMPING_SUPPORTED 1505 #include <AssertMacros.h> 1506 /* 1507 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if 1508 * present. This timestamp is an authenticated timestamp provided by 1509 * a timestamping authority. 1510 * 1511 * Returns errSecParam if the CMS message was not signed or if signerIndex 1512 * is greater than the number of signers of the message minus one. 1513 * 1514 * This cannot be called until after CMSEncoderCopyEncodedContent() is called. 1515 */ 1516 OSStatus CMSEncoderCopySignerTimestamp( 1517 CMSEncoderRef cmsEncoder, 1518 size_t signerIndex, /* usually 0 */ 1519 CFAbsoluteTime *timestamp) /* RETURNED */ 1520 { 1521 return CMSEncoderCopySignerTimestampWithPolicy( 1522 cmsEncoder, 1523 NULL, 1524 signerIndex, 1525 timestamp); 1526 } 1527 1528 OSStatus CMSEncoderCopySignerTimestampWithPolicy( 1529 CMSEncoderRef cmsEncoder, 1530 CFTypeRef timeStampPolicy, 1531 size_t signerIndex, /* usually 0 */ 1532 CFAbsoluteTime *timestamp) /* RETURNED */ 1533 { 1534 OSStatus status = errSecParam; 1535 SecCmsMessageRef cmsg; 1536 SecCmsSignedDataRef signedData = NULL; 1537 int numContentInfos = 0; 1538 1539 require(cmsEncoder && timestamp, xit); 1540 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder, &cmsg), xit); 1541 numContentInfos = SecCmsMessageContentLevelCount(cmsg); 1542 for (int dex = 0; !signedData && dex < numContentInfos; dex++) 1543 { 1544 SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); 1545 SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); 1546 if (tag == SEC_OID_PKCS7_SIGNED_DATA) 1547 if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { 1548 SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); 1549 if (signerInfo) 1550 { 1551 status = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp); 1552 break; 1553 } 1554 } 1555 } 1556 1557 xit: 1558 return status; 1559 } 1560 #endif