2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * CMSEncoder.cpp - encode, sign, and/or encrypt CMS messages.
28 #include <Security/CMSEncoder.h>
29 #include <Security/CMSPrivate.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>
50 #include <utilities/SecCFRelease.h>
52 #include <security_smime/tsaSupport.h>
53 #include <security_smime/cmspriv.h>
55 #pragma mark --- Private types and definitions ---
61 ES_Init
, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent
62 * and CMSEncodeGetCmsMessage */
63 ES_Msg
, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */
64 ES_Updating
, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */
65 ES_Final
/* CMSEncoderCopyEncodedContent has been called */
69 * High-level operation: what are we doing?
78 * Caller's CMSEncoderRef points to one of these.
82 CMSEncoderState encState
;
84 Boolean detachedContent
;
85 CSSM_OID eContentType
;
86 CFMutableArrayRef signers
;
87 CFMutableArrayRef recipients
;
88 CFMutableArrayRef otherCerts
;
89 CMSSignedAttributes signedAttributes
;
90 CFAbsoluteTime signingTime
;
91 SecCmsMessageRef cmsMsg
;
92 SecArenaPoolRef arena
; /* the encoder's arena */
93 SecCmsEncoderRef encoder
;
94 CSSM_DATA encoderOut
; /* output goes here... */
95 bool customCoder
; /* unless this is set by
96 * CMSEncoderSetEncoder */
97 SECOidTag digestalgtag
;
99 CMSCertificateChainMode chainMode
;
100 CFDataRef hashAgilityAttrValue
;
101 CFDictionaryRef hashAgilityV2AttrValues
;
102 CFAbsoluteTime expirationTime
;
105 static void cmsEncoderInit(CFTypeRef enc
);
106 static void cmsEncoderFinalize(CFTypeRef enc
);
108 static CFRuntimeClass cmsEncoderRuntimeClass
=
115 NULL
, /* equal - just use pointer equality */
116 NULL
, /* hash, ditto */
117 NULL
, /* copyFormattingDesc */
118 NULL
/* copyDebugDesc */
122 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
124 #pragma mark --- Private routines ---
127 * Decode a CFStringRef representation of an integer
129 static int cfStringToNumber(
134 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
140 * Encode an integer component of an OID, return resulting number of bytes;
141 * actual bytes are mallocd and returned in *encodeArray.
143 static unsigned encodeNumber(
145 unsigned char **encodeArray
) // mallocd and RETURNED
147 unsigned char *result
;
149 unsigned numDigits
= 0;
152 /* trival case - 0 maps to 0 */
154 *encodeArray
= (unsigned char *)malloc(1);
159 /* first calculate the number of digits in num, base 128 */
160 scratch
= (unsigned)num
;
161 while(scratch
!= 0) {
166 result
= (unsigned char *)malloc(numDigits
);
167 scratch
= (unsigned)num
;
168 for(dex
=0; dex
<numDigits
; dex
++) {
169 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
173 /* all digits except the last one have m.s. bit set */
174 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
178 *encodeArray
= result
;
183 * Given an OID in dotted-decimal string representation, convert to binary
184 * DER format. Returns a pointer in outOid which the caller must free(),
185 * as well as the length of the data in outLen.
186 * Function returns 0 if successful, non-zero otherwise.
188 static int encodeOid(
189 const unsigned char *inStr
,
190 unsigned char **outOid
,
191 unsigned int *outLen
)
193 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
194 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
196 unsigned numDigitBytes
; /* total #of output chars */
197 unsigned char firstByte
;
199 CFIndex numsToProcess
;
200 CFStringRef oidStr
= NULL
;
201 CFArrayRef argvRef
= NULL
;
205 /* parse input string into array of substrings */
206 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
207 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
208 if (!oidStr
) goto cleanExit
;
209 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
210 if (!argvRef
) goto cleanExit
;
211 argc
= CFArrayGetCount(argvRef
);
212 if (argc
< 3) goto cleanExit
;
214 /* first two numbers in OID munge together */
215 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
216 if (num
< 0) goto cleanExit
;
217 firstByte
= (40 * num
);
218 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
219 if (num
< 0) goto cleanExit
;
223 numsToProcess
= argc
- 2;
224 if(numsToProcess
> 0) {
225 /* skip this loop in the unlikely event that input is only two numbers */
226 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
227 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
228 for(digit
=0; digit
<numsToProcess
; digit
++) {
229 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
230 if (num
< 0) goto cleanExit
;
231 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
232 numDigitBytes
+= numDigits
[digit
];
235 *outLen
= (2 + numDigitBytes
);
236 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
238 *outP
++ = numDigitBytes
;
240 for(digit
=0; digit
<numsToProcess
; digit
++) {
241 unsigned int byteDex
;
242 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
243 *outP
++ = digits
[digit
][byteDex
];
247 for(digit
=0; digit
<numsToProcess
; digit
++) {
254 if (digits
) free(digits
);
255 if (numDigits
) free(numDigits
);
256 if (oidStr
) CFRelease(oidStr
);
257 if (argvRef
) CFRelease(argvRef
);
263 * Given a CF object reference describing an OID, convert to binary DER format
264 * and fill out the CSSM_OID structure provided by the caller. Caller is
265 * responsible for freeing the data pointer in outOid->Data.
267 * Function returns 0 if successful, non-zero otherwise.
270 static int convertOid(
274 if (!inRef
|| !outOid
)
277 unsigned char *oidData
= NULL
;
278 unsigned int oidLen
= 0;
280 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
281 // CFStringRef: OID representation is a dotted-decimal string
282 CFStringRef inStr
= (CFStringRef
)inRef
;
283 CFIndex max
= CFStringGetLength(inStr
) * 3;
284 char *buf
= (char *)malloc(max
);
286 return errSecMemoryError
;
288 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
)) {
293 if (encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0) {
299 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
300 // CFDataRef: OID representation is in binary DER format
301 CFDataRef inData
= (CFDataRef
)inRef
;
302 oidLen
= (unsigned int) CFDataGetLength(inData
);
303 oidData
= (unsigned char *) malloc(oidLen
);
304 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
307 // Not in a format we understand
310 outOid
->Length
= oidLen
;
311 outOid
->Data
= (uint8
*)oidData
;
315 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
317 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
318 static void cmsEncoderClassInitialize(void)
321 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
324 /* init called out from _CFRuntimeCreateInstance() */
325 static void cmsEncoderInit(CFTypeRef enc
)
327 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
328 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
332 * Dispose of a CMSEncoder. Called out from CFRelease().
334 static void cmsEncoderFinalize(
337 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
338 if(cmsEncoder
== NULL
) {
341 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
342 free(cmsEncoder
->eContentType
.Data
);
344 CFRELEASE(cmsEncoder
->signers
);
345 CFRELEASE(cmsEncoder
->recipients
);
346 CFRELEASE(cmsEncoder
->otherCerts
);
347 if(cmsEncoder
->cmsMsg
!= NULL
) {
348 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
349 cmsEncoder
->cmsMsg
= NULL
;
351 if(cmsEncoder
->arena
!= NULL
) {
352 SecArenaPoolFree(cmsEncoder
->arena
, false);
354 if(cmsEncoder
->encoder
!= NULL
) {
356 * Normally this gets freed in SecCmsEncoderFinish - this is
359 SecCmsEncoderDestroy(cmsEncoder
->encoder
);
363 static OSStatus
cmsSetupEncoder(
364 CMSEncoderRef cmsEncoder
)
368 ASSERT(cmsEncoder
->arena
== NULL
);
369 ASSERT(cmsEncoder
->encoder
== NULL
);
371 ortn
= SecArenaPoolCreate(1024, &cmsEncoder
->arena
);
373 return cmsRtnToOSStatus(ortn
);
375 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
376 NULL
, NULL
, // no callback
377 &cmsEncoder
->encoderOut
, // data goes here
379 NULL
, NULL
, // no password callback (right?)
380 NULL
, NULL
, // decrypt key callback
381 NULL
, NULL
, // detached digests
382 &cmsEncoder
->encoder
);
384 return cmsRtnToOSStatus(ortn
);
386 return errSecSuccess
;
390 * Set up a SecCmsMessageRef for a SignedData creation.
392 static OSStatus
cmsSetupForSignedData(
393 CMSEncoderRef cmsEncoder
)
395 ASSERT((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
));
397 SecCmsContentInfoRef contentInfo
= NULL
;
398 SecCmsSignedDataRef signedData
= NULL
;
401 /* build chain of objects: message->signedData->data */
402 if(cmsEncoder
->cmsMsg
!= NULL
) {
403 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
405 cmsEncoder
->cmsMsg
= SecCmsMessageCreate(NULL
);
406 if(cmsEncoder
->cmsMsg
== NULL
) {
407 return errSecInternalComponent
;
410 signedData
= SecCmsSignedDataCreate(cmsEncoder
->cmsMsg
);
411 if(signedData
== NULL
) {
412 return errSecInternalComponent
;
414 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
415 ortn
= SecCmsContentInfoSetContentSignedData(cmsEncoder
->cmsMsg
, contentInfo
,
418 return cmsRtnToOSStatus(ortn
);
420 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
421 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
422 /* Override the default eContentType of id-data */
423 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
425 NULL
, /* data - provided to encoder, not here */
426 cmsEncoder
->detachedContent
,
427 &cmsEncoder
->eContentType
);
430 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
432 NULL
, /* data - provided to encoder, not here */
433 cmsEncoder
->detachedContent
);
436 ortn
= cmsRtnToOSStatus(ortn
);
437 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
441 /* optional 'global' (per-SignedData) certs */
442 if(cmsEncoder
->otherCerts
!= NULL
) {
443 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
445 ortn
= cmsRtnToOSStatus(ortn
);
446 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
451 /* SignerInfos, one per signer */
452 CFIndex numSigners
= 0;
453 if(cmsEncoder
->signers
!= NULL
) {
454 /* this is optional...in case we're just creating a cert bundle */
455 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
458 SecCertificateRef ourCert
= NULL
;
459 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
461 switch(cmsEncoder
->chainMode
) {
462 case kCMSCertificateNone
:
463 chainMode
= SecCmsCMNone
;
465 case kCMSCertificateSignerOnly
:
466 chainMode
= SecCmsCMCertOnly
;
468 case kCMSCertificateChainWithRoot
:
469 chainMode
= SecCmsCMCertChainWithRoot
;
471 case kCMSCertificateChainWithRootOrFail
:
472 chainMode
= SecCmsCMCertChainWithRootOrFail
;
477 for(dex
=0; dex
<numSigners
; dex
++) {
478 SecCmsSignerInfoRef signerInfo
;
480 SecIdentityRef ourId
=
481 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
482 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
484 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
487 signerInfo
= SecCmsSignerInfoCreate(cmsEncoder
->cmsMsg
, ourId
, cmsEncoder
->digestalgtag
);
488 if (signerInfo
== NULL
) {
489 ortn
= errSecInternalComponent
;
493 /* we want the cert chain included for this one */
494 /* NOTE the usage parameter is currently unused by the SMIME lib */
495 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
496 certUsageEmailSigner
);
498 ortn
= cmsRtnToOSStatus(ortn
);
499 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
504 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
505 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
507 ortn
= cmsRtnToOSStatus(ortn
);
508 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
512 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
513 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
515 ortn
= cmsRtnToOSStatus(ortn
);
516 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
520 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
521 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
523 ortn
= cmsRtnToOSStatus(ortn
);
524 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
528 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
529 if (cmsEncoder
->signingTime
== 0)
530 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
531 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
533 ortn
= cmsRtnToOSStatus(ortn
);
534 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
538 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgility
) {
539 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo
, cmsEncoder
->hashAgilityAttrValue
);
540 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
541 CFReleaseNull(cmsEncoder
->hashAgilityAttrValue
);
543 ortn
= cmsRtnToOSStatus(ortn
);
544 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn
);
548 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgilityV2
) {
549 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(signerInfo
, cmsEncoder
->hashAgilityV2AttrValues
);
550 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
551 CFReleaseNull(cmsEncoder
->hashAgilityV2AttrValues
);
553 ortn
= cmsRtnToOSStatus(ortn
);
554 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgilityV2", ortn
);
558 if (cmsEncoder
->signedAttributes
& kCMSAttrAppleExpirationTime
) {
559 ortn
= SecCmsSignerInfoAddAppleExpirationTime(signerInfo
, cmsEncoder
->expirationTime
);
561 ortn
= cmsRtnToOSStatus(ortn
);
562 CSSM_PERROR("SecCmsSignerInfoAddAppleExpirationTime", ortn
);
567 ortn
= SecCmsSignedDataAddSignerInfo(signedData
, signerInfo
);
569 ortn
= cmsRtnToOSStatus(ortn
);
570 CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn
);
584 * Set up a SecCmsMessageRef for a EnvelopedData creation.
586 static OSStatus
cmsSetupForEnvelopedData(
587 CMSEncoderRef cmsEncoder
)
589 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
590 ASSERT(cmsEncoder
->recipients
!= NULL
);
592 SecCmsContentInfoRef contentInfo
= NULL
;
593 SecCmsEnvelopedDataRef envelopedData
= NULL
;
594 SECOidTag algorithmTag
;
599 * Find encryption algorithm...unfortunately we need a NULL-terminated array
600 * of SecCertificateRefs for this.
602 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
604 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
605 (numCerts
+1) * sizeof(SecCertificateRef
));
607 for(dex
=0; dex
<numCerts
; dex
++) {
608 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
609 cmsEncoder
->recipients
, dex
);
611 certArray
[numCerts
] = NULL
;
612 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
615 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
619 /* build chain of objects: message->envelopedData->data */
620 if(cmsEncoder
->cmsMsg
!= NULL
) {
621 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
623 cmsEncoder
->cmsMsg
= SecCmsMessageCreate(NULL
);
624 if(cmsEncoder
->cmsMsg
== NULL
) {
625 return errSecInternalComponent
;
627 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
628 algorithmTag
, keySize
);
629 if(envelopedData
== NULL
) {
630 return errSecInternalComponent
;
632 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
633 ortn
= SecCmsContentInfoSetContentEnvelopedData(cmsEncoder
->cmsMsg
,
634 contentInfo
, envelopedData
);
636 ortn
= cmsRtnToOSStatus(ortn
);
637 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
640 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
641 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
642 /* Override the default ContentType of id-data */
643 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
645 NULL
, /* data - provided to encoder, not here */
646 FALSE
, /* detachedContent */
647 &cmsEncoder
->eContentType
);
650 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
652 NULL
/* data - provided to encoder, not here */,
653 cmsEncoder
->detachedContent
);
656 ortn
= cmsRtnToOSStatus(ortn
);
657 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
662 * create & attach recipient information, one for each recipient
664 for(dex
=0; dex
<numCerts
; dex
++) {
665 SecCmsRecipientInfoRef recipientInfo
= NULL
;
667 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
668 cmsEncoder
->recipients
, dex
);
669 recipientInfo
= SecCmsRecipientInfoCreate(cmsEncoder
->cmsMsg
, thisRecip
);
670 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
672 ortn
= cmsRtnToOSStatus(ortn
);
673 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
677 return errSecSuccess
;
681 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
682 * from CMSEncodeGetCmsMessage().
684 static OSStatus
cmsSetupCmsMsg(
685 CMSEncoderRef cmsEncoder
)
687 ASSERT(cmsEncoder
!= NULL
);
688 ASSERT(cmsEncoder
->encState
== ES_Init
);
690 /* figure out what high-level operation we're doing */
691 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
692 if(cmsEncoder
->recipients
!= NULL
) {
693 cmsEncoder
->op
= EO_SignEncrypt
;
696 cmsEncoder
->op
= EO_Sign
;
699 else if(cmsEncoder
->recipients
!= NULL
) {
700 cmsEncoder
->op
= EO_Encrypt
;
703 dprintf("CMSEncoderUpdateContent: nothing to do\n");
707 OSStatus ortn
= errSecSuccess
;
709 switch(cmsEncoder
->op
) {
712 /* If we're signing & encrypting, do the signing first */
713 ortn
= cmsSetupForSignedData(cmsEncoder
);
716 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
719 cmsEncoder
->encState
= ES_Msg
;
724 * ASN.1 template for decoding a ContentInfo.
727 CSSM_OID contentType
;
731 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
732 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
733 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
734 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
735 offsetof(SimpleContentInfo
, content
),
736 kSecAsn1AnyTemplate
},
741 * Obtain the content of a contentInfo, This basically strips off the contentType OID
742 * and returns its ASN_ANY content, allocated the provided coder's memory space.
744 static OSStatus
cmsContentInfoContent(
745 SecAsn1CoderRef asn1Coder
,
746 const CSSM_DATA
*contentInfo
,
747 CSSM_DATA
*content
) /* RETURNED */
750 SimpleContentInfo decodedInfo
;
752 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
753 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
754 cmsSimpleContentInfoTemplate
, &decodedInfo
);
758 if(decodedInfo
.content
.Data
== NULL
) {
759 dprintf("***Error decoding contentInfo: no content\n");
760 return errSecInternalComponent
;
762 *content
= decodedInfo
.content
;
763 return errSecSuccess
;
766 #pragma mark --- Start of Public API ---
768 CFTypeID
CMSEncoderGetTypeID(void)
770 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
772 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
773 pthread_once(&once
, &cmsEncoderClassInitialize
);
775 return cmsEncoderTypeID
;
779 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
781 OSStatus
CMSEncoderCreate(
782 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
784 CMSEncoderRef cmsEncoder
= NULL
;
786 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
787 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
789 if(cmsEncoder
== NULL
) {
790 return errSecAllocate
;
792 cmsEncoder
->encState
= ES_Init
;
793 cmsEncoder
->chainMode
= kCMSCertificateChain
;
794 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
795 *cmsEncoderOut
= cmsEncoder
;
796 return errSecSuccess
;
799 #pragma mark --- Getters & Setters ---
801 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1
= CFSTR("sha1");
802 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256
= CFSTR("sha256");
805 OSStatus
CMSEncoderSetSignerAlgorithm(
806 CMSEncoderRef cmsEncoder
,
807 CFStringRef digestAlgorithm
)
809 if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA1
)) {
810 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
811 } else if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA256
)) {
812 cmsEncoder
->digestalgtag
= SEC_OID_SHA256
;
817 return errSecSuccess
;
821 * Specify signers of the CMS message; implies that the message will be signed.
823 OSStatus
CMSEncoderAddSigners(
824 CMSEncoderRef cmsEncoder
,
825 CFTypeRef signerOrArray
)
827 if(cmsEncoder
== NULL
) {
830 if(cmsEncoder
->encState
!= ES_Init
) {
833 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
837 * Obtain an array of signers as specified in CMSEncoderSetSigners().
839 OSStatus
CMSEncoderCopySigners(
840 CMSEncoderRef cmsEncoder
,
843 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
846 if(cmsEncoder
->signers
!= NULL
) {
847 CFRetain(cmsEncoder
->signers
);
849 *signers
= cmsEncoder
->signers
;
850 return errSecSuccess
;
854 * Specify recipients of the message. Implies that the message will be encrypted.
856 OSStatus
CMSEncoderAddRecipients(
857 CMSEncoderRef cmsEncoder
,
858 CFTypeRef recipientOrArray
)
860 if(cmsEncoder
== NULL
) {
863 if(cmsEncoder
->encState
!= ES_Init
) {
866 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
867 SecCertificateGetTypeID());
871 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
873 OSStatus
CMSEncoderCopyRecipients(
874 CMSEncoderRef cmsEncoder
,
875 CFArrayRef
*recipients
)
877 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
880 if(cmsEncoder
->recipients
!= NULL
) {
881 CFRetain(cmsEncoder
->recipients
);
883 *recipients
= cmsEncoder
->recipients
;
884 return errSecSuccess
;
888 * Specify additional certs to include in a signed message.
890 OSStatus
CMSEncoderAddSupportingCerts(
891 CMSEncoderRef cmsEncoder
,
892 CFTypeRef certOrArray
)
894 if(cmsEncoder
== NULL
) {
897 if(cmsEncoder
->encState
!= ES_Init
) {
900 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
901 SecCertificateGetTypeID());
905 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
907 OSStatus
CMSEncoderCopySupportingCerts(
908 CMSEncoderRef cmsEncoder
,
909 CFArrayRef
*certs
) /* RETURNED */
911 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
914 if(cmsEncoder
->otherCerts
!= NULL
) {
915 CFRetain(cmsEncoder
->otherCerts
);
917 *certs
= cmsEncoder
->otherCerts
;
918 return errSecSuccess
;
921 OSStatus
CMSEncoderSetHasDetachedContent(
922 CMSEncoderRef cmsEncoder
,
923 Boolean detachedContent
)
925 if(cmsEncoder
== NULL
) {
928 if(cmsEncoder
->encState
!= ES_Init
) {
931 cmsEncoder
->detachedContent
= detachedContent
;
932 return errSecSuccess
;
935 OSStatus
CMSEncoderGetHasDetachedContent(
936 CMSEncoderRef cmsEncoder
,
937 Boolean
*detachedContent
) /* RETURNED */
939 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
942 *detachedContent
= cmsEncoder
->detachedContent
;
943 return errSecSuccess
;
947 * Optionally specify an eContentType OID for the inner EncapsulatedData for
948 * a signed message. The default eContentType, used of this function is not
949 * called, is id-data.
951 OSStatus
CMSEncoderSetEncapsulatedContentType(
952 CMSEncoderRef cmsEncoder
,
953 const CSSM_OID
*eContentType
)
955 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
958 if(cmsEncoder
->encState
!= ES_Init
) {
962 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
963 if(ecOid
->Data
!= NULL
) {
966 cmsCopyCmsData(eContentType
, ecOid
);
967 return errSecSuccess
;
970 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
971 CMSEncoderRef cmsEncoder
,
972 CFTypeRef eContentTypeOID
)
974 // convert eContentTypeOID to a CSSM_OID
975 CSSM_OID contentType
= { 0, NULL
};
976 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
978 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
979 if (contentType
.Data
)
980 free(contentType
.Data
);
985 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
987 OSStatus
CMSEncoderCopyEncapsulatedContentType(
988 CMSEncoderRef cmsEncoder
,
989 CFDataRef
*eContentType
)
991 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
995 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
996 if(ecOid
->Data
== NULL
) {
997 *eContentType
= NULL
;
1000 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
1002 return errSecSuccess
;
1006 * Optionally specify signed attributes. Only meaningful when creating a
1007 * signed message. If this is called, it must be called before
1008 * CMSEncoderUpdateContent().
1010 OSStatus
CMSEncoderAddSignedAttributes(
1011 CMSEncoderRef cmsEncoder
,
1012 CMSSignedAttributes signedAttributes
)
1014 if(cmsEncoder
== NULL
) {
1017 if(cmsEncoder
->encState
!= ES_Init
) {
1020 cmsEncoder
->signedAttributes
|= signedAttributes
;
1021 return errSecSuccess
;
1025 * Set the signing time for a CMSEncoder.
1026 * This is only used if the kCMSAttrSigningTime attribute is included.
1028 OSStatus
CMSEncoderSetSigningTime(
1029 CMSEncoderRef cmsEncoder
,
1030 CFAbsoluteTime time
)
1032 if(cmsEncoder
== NULL
) {
1035 if(cmsEncoder
->encState
!= ES_Init
) {
1038 cmsEncoder
->signingTime
= time
;
1039 return errSecSuccess
;
1043 * Set the hash agility attribute for a CMSEncoder.
1044 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute
1047 OSStatus
CMSEncoderSetAppleCodesigningHashAgility(
1048 CMSEncoderRef cmsEncoder
,
1049 CFDataRef hashAgilityAttrValue
)
1051 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1054 cmsEncoder
->hashAgilityAttrValue
= CFRetainSafe(hashAgilityAttrValue
);
1055 return errSecSuccess
;
1059 * Set the hash agility attribute for a CMSEncoder.
1060 * This is only used if the kCMSAttrAppleCodesigningHashAgilityV2 attribute
1063 OSStatus
CMSEncoderSetAppleCodesigningHashAgilityV2(
1064 CMSEncoderRef cmsEncoder
,
1065 CFDictionaryRef hashAgilityV2AttrValues
)
1067 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1070 cmsEncoder
->hashAgilityV2AttrValues
= CFRetainSafe(hashAgilityV2AttrValues
);
1071 return errSecSuccess
;
1075 * Set the expiration time for a CMSEncoder.
1076 * This is only used if the kCMSAttrAppleExpirationTime attribute is included.
1078 OSStatus
CMSEncoderSetAppleExpirationTime(
1079 CMSEncoderRef cmsEncoder
,
1080 CFAbsoluteTime time
)
1082 if(cmsEncoder
== NULL
) {
1085 if(cmsEncoder
->encState
!= ES_Init
) {
1088 cmsEncoder
->expirationTime
= time
;
1089 return errSecSuccess
;
1092 OSStatus
CMSEncoderSetCertificateChainMode(
1093 CMSEncoderRef cmsEncoder
,
1094 CMSCertificateChainMode chainMode
)
1096 if(cmsEncoder
== NULL
) {
1099 if(cmsEncoder
->encState
!= ES_Init
) {
1103 case kCMSCertificateNone
:
1104 case kCMSCertificateSignerOnly
:
1105 case kCMSCertificateChain
:
1106 case kCMSCertificateChainWithRoot
:
1107 case kCMSCertificateChainWithRootOrFail
:
1112 cmsEncoder
->chainMode
= chainMode
;
1113 return errSecSuccess
;
1116 OSStatus
CMSEncoderGetCertificateChainMode(
1117 CMSEncoderRef cmsEncoder
,
1118 CMSCertificateChainMode
*chainModeOut
)
1120 if(cmsEncoder
== NULL
) {
1123 *chainModeOut
= cmsEncoder
->chainMode
;
1124 return errSecSuccess
;
1128 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1130 if (cmsEncoder
->cmsMsg
)
1131 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1135 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1137 if (cmsEncoder
->cmsMsg
)
1138 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1141 #pragma mark --- Action ---
1144 * Feed content bytes into the encoder.
1145 * Can be called multiple times.
1146 * No 'setter' routines can be called after this function has been called.
1148 OSStatus
CMSEncoderUpdateContent(
1149 CMSEncoderRef cmsEncoder
,
1150 const void *content
,
1153 if(cmsEncoder
== NULL
) {
1157 OSStatus ortn
= errSecSuccess
;
1158 switch(cmsEncoder
->encState
) {
1161 * First time thru: do the CmsMsg setup.
1163 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1167 /* fall thru to set up the encoder */
1170 /* We have a cmsMsg but no encoder; create one */
1171 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1172 ASSERT(cmsEncoder
->encoder
== NULL
);
1173 ortn
= cmsSetupEncoder(cmsEncoder
);
1177 /* only legal calls now are update and finalize */
1178 cmsEncoder
->encState
= ES_Updating
;
1182 ASSERT(cmsEncoder
->encoder
!= NULL
);
1186 /* Too late for another update */
1190 return errSecInternalComponent
;
1193 /* FIXME - CFIndex same size as size_t on 64bit? */
1194 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1196 ortn
= cmsRtnToOSStatus(ortn
);
1197 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1203 * Finish encoding the message and obtain the encoded result.
1204 * Caller must CFRelease the result.
1206 OSStatus
CMSEncoderCopyEncodedContent(
1207 CMSEncoderRef cmsEncoder
,
1208 CFDataRef
*encodedContent
)
1210 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1216 switch(cmsEncoder
->encState
) {
1218 /* normal termination */
1221 /* already been called */
1226 * The only time these are legal is when we're doing a SignedData
1227 * with certificates only (no signers, no content).
1229 if((cmsEncoder
->signers
!= NULL
) ||
1230 (cmsEncoder
->recipients
!= NULL
) ||
1231 (cmsEncoder
->otherCerts
== NULL
)) {
1235 /* Set up for certs only */
1236 ortn
= cmsSetupForSignedData(cmsEncoder
);
1240 /* and an encoder */
1241 ortn
= cmsSetupEncoder(cmsEncoder
);
1249 ASSERT(cmsEncoder
->encoder
!= NULL
);
1250 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1251 /* regardless of the outcome, the encoder itself has been freed */
1252 cmsEncoder
->encoder
= NULL
;
1254 return cmsRtnToOSStatus(ortn
);
1256 cmsEncoder
->encState
= ES_Final
;
1258 if((cmsEncoder
->encoderOut
.Data
== NULL
) && !cmsEncoder
->customCoder
) {
1259 /* not sure how this could happen... */
1260 dprintf("Successful encode, but no data\n");
1261 return errSecInternalComponent
;
1263 if(cmsEncoder
->customCoder
) {
1265 *encodedContent
= NULL
;
1266 return errSecSuccess
;
1269 /* in two out of three cases, we're done */
1270 switch(cmsEncoder
->op
) {
1273 *encodedContent
= CFDataCreate(NULL
, (const UInt8
*)cmsEncoder
->encoderOut
.Data
,
1274 cmsEncoder
->encoderOut
.Length
);
1275 return errSecSuccess
;
1276 case EO_SignEncrypt
:
1277 /* proceed, more work to do */
1282 * Signing & encrypting.
1283 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1284 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1285 * inside of the ContentInfo we just created as the data to encrypt.
1287 SecAsn1CoderRef asn1Coder
= NULL
;
1288 CSSM_DATA signedData
= {0, NULL
};
1290 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1294 ortn
= cmsContentInfoContent(asn1Coder
, &cmsEncoder
->encoderOut
, &signedData
);
1299 /* now just encrypt that, one-shot */
1300 ortn
= CMSEncode(NULL
, /* no signers this time */
1301 cmsEncoder
->recipients
,
1302 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1303 * encode the signedData - this asserts the
1304 * SEC_OID_OTHER OID tag in the EnvelopedData's
1306 FALSE
, /* detachedContent */
1307 kCMSAttrNone
, /* signedAttributes - none this time */
1308 signedData
.Data
, signedData
.Length
,
1313 SecAsn1CoderRelease(asn1Coder
);
1318 #pragma mark --- High-level API ---
1321 * High-level, one-shot encoder function.
1325 CFTypeRef recipients
,
1326 const CSSM_OID
*eContentType
,
1327 Boolean detachedContent
,
1328 CMSSignedAttributes signedAttributes
,
1329 const void *content
,
1331 CFDataRef
*encodedContent
) /* RETURNED */
1333 if((signers
== NULL
) && (recipients
== NULL
)) {
1336 if(encodedContent
== NULL
) {
1340 CMSEncoderRef cmsEncoder
;
1343 /* set up the encoder */
1344 ortn
= CMSEncoderCreate(&cmsEncoder
);
1349 /* subsequent errors to errOut: */
1351 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1357 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1363 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1368 if(detachedContent
) {
1369 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1374 if(signedAttributes
) {
1375 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1381 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1385 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1388 CFRelease(cmsEncoder
);
1392 OSStatus
CMSEncodeContent(
1394 CFTypeRef recipients
,
1395 CFTypeRef eContentTypeOID
,
1396 Boolean detachedContent
,
1397 CMSSignedAttributes signedAttributes
,
1398 const void *content
,
1400 CFDataRef
*encodedContentOut
) /* RETURNED */
1402 // convert eContentTypeOID to a CSSM_OID
1403 CSSM_OID contentType
= { 0, NULL
};
1404 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1406 const CSSM_OID
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1407 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1408 detachedContent
, signedAttributes
,
1409 content
, contentLen
, encodedContentOut
);
1410 if (contentType
.Data
)
1411 free(contentType
.Data
);
1415 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1418 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1419 * If we don't have a SecCmsMessageRef yet, we create one now.
1420 * This is the only place where we go to state ES_Msg.
1422 OSStatus
CMSEncoderGetCmsMessage(
1423 CMSEncoderRef cmsEncoder
,
1424 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1426 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1429 if(cmsEncoder
->cmsMsg
!= NULL
) {
1430 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1431 *cmsMessage
= cmsEncoder
->cmsMsg
;
1432 return errSecSuccess
;
1435 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1439 *cmsMessage
= cmsEncoder
->cmsMsg
;
1441 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1442 cmsEncoder
->encState
= ES_Msg
;
1443 return errSecSuccess
;
1447 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1448 * If this is called, it must be called before the first call to
1449 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1450 * incoming SecCmsEncoderRef.
1452 OSStatus
CMSEncoderSetEncoder(
1453 CMSEncoderRef cmsEncoder
,
1454 SecCmsEncoderRef encoder
)
1456 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1462 switch(cmsEncoder
->encState
) {
1464 /* No message, no encoder */
1465 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1466 ASSERT(cmsEncoder
->encoder
== NULL
);
1467 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1471 /* drop thru to set encoder */
1473 /* cmsMsg but no encoder */
1474 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1475 ASSERT(cmsEncoder
->encoder
== NULL
);
1476 cmsEncoder
->encoder
= encoder
;
1477 cmsEncoder
->encState
= ES_Updating
;
1478 cmsEncoder
->customCoder
= true; /* we won't see data */
1479 return errSecSuccess
;
1481 /* no can do, too late */
1487 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1488 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1489 * CMSEncoderUpdateContent() has been called.
1490 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1492 OSStatus
CMSEncoderGetEncoder(
1493 CMSEncoderRef cmsEncoder
,
1494 SecCmsEncoderRef
*encoder
) /* RETURNED */
1496 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1500 /* any state, whether we have an encoder or not is OK */
1501 *encoder
= cmsEncoder
->encoder
;
1502 return errSecSuccess
;
1505 #include <AssertMacros.h>
1508 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1509 * present. This timestamp is an authenticated timestamp provided by
1510 * a timestamping authority.
1512 * Returns errSecParam if the CMS message was not signed or if signerIndex
1513 * is greater than the number of signers of the message minus one.
1515 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1517 OSStatus
CMSEncoderCopySignerTimestamp(
1518 CMSEncoderRef cmsEncoder
,
1519 size_t signerIndex
, /* usually 0 */
1520 CFAbsoluteTime
*timestamp
) /* RETURNED */
1522 return CMSEncoderCopySignerTimestampWithPolicy(
1529 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1530 CMSEncoderRef cmsEncoder
,
1531 CFTypeRef timeStampPolicy
,
1532 size_t signerIndex
, /* usually 0 */
1533 CFAbsoluteTime
*timestamp
) /* RETURNED */
1535 OSStatus status
= errSecParam
;
1536 SecCmsMessageRef cmsg
;
1537 SecCmsSignedDataRef signedData
= NULL
;
1538 int numContentInfos
= 0;
1540 require(cmsEncoder
&& timestamp
, xit
);
1541 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1542 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1543 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1545 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1546 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1547 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1548 if ((signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
))))
1549 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
))
1551 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);