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