2 * Copyright (c) 2006-2016 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.c - encode, sign, and/or encrypt CMS messages.
28 #include "CMSEncoder.h"
30 #include <Security/SecBase.h>
31 #include <Security/SecCmsEncoder.h>
32 #include <Security/SecCmsEnvelopedData.h>
33 #include <Security/SecCmsMessage.h>
34 #include <Security/SecCmsRecipientInfo.h>
35 #include <Security/SecCmsSignedData.h>
36 #include <Security/SecCmsSignerInfo.h>
37 #include <Security/SecCmsContentInfo.h>
38 #include <Security/SecCertificate.h>
39 #include <Security/SecIdentity.h>
40 #include <Security/SecSMIME.h>
41 #include <Security/oidsattr.h>
42 #include <Security/SecAsn1Coder.h>
43 #include <Security/SecAsn1Types.h>
44 #include <Security/SecAsn1Templates.h>
45 #include <CoreFoundation/CFRuntime.h>
47 #include <utilities/SecCFRelease.h>
49 #include <security_smime/cmspriv.h>
51 #if TIMESTAMPING_SUPPORTED
52 #include <security_smime/tsaSupport.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 SecAsn1Oid eContentType
;
86 CFMutableArrayRef signers
;
87 CFMutableArrayRef recipients
;
88 CFMutableArrayRef otherCerts
;
89 CMSSignedAttributes signedAttributes
;
90 CFAbsoluteTime signingTime
;
91 SecCmsMessageRef cmsMsg
; /* the encoder's arena */
92 SecCmsEncoderRef encoder
;
93 CFMutableDataRef encoderOut
; /* output goes here... */
94 bool customCoder
; /* unless this is set by
95 * CMSEncoderSetEncoder */
96 SECOidTag digestalgtag
;
98 CMSCertificateChainMode chainMode
;
99 CFDataRef hashAgilityAttrValue
;
100 CFDictionaryRef hashAgilityV2AttrValues
;
103 static void cmsEncoderInit(CFTypeRef enc
);
104 static void cmsEncoderFinalize(CFTypeRef enc
);
106 static CFRuntimeClass cmsEncoderRuntimeClass
=
113 NULL
, /* equal - just use pointer equality */
114 NULL
, /* hash, ditto */
115 NULL
, /* copyFormattingDesc */
116 NULL
/* copyDebugDesc */
119 #if TIMESTAMPING_SUPPORTED
120 void CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
123 #pragma mark --- Private routines ---
126 * Decode a CFStringRef representation of an integer
128 static int cfStringToNumber(
133 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
139 * Encode an integer component of an OID, return resulting number of bytes;
140 * actual bytes are mallocd and returned in *encodeArray.
142 static unsigned encodeNumber(
144 unsigned char **encodeArray
) // mallocd and RETURNED
146 unsigned char *result
;
148 unsigned numDigits
= 0;
151 /* trival case - 0 maps to 0 */
153 *encodeArray
= (unsigned char *)malloc(1);
158 /* first calculate the number of digits in num, base 128 */
159 scratch
= (unsigned)num
;
160 while(scratch
!= 0) {
165 result
= (unsigned char *)malloc(numDigits
);
166 scratch
= (unsigned)num
;
167 for(dex
=0; dex
<numDigits
; dex
++) {
168 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
172 /* all digits except the last one have m.s. bit set */
173 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
177 *encodeArray
= result
;
182 * Given an OID in dotted-decimal string representation, convert to binary
183 * DER format. Returns a pointer in outOid which the caller must free(),
184 * as well as the length of the data in outLen.
185 * Function returns 0 if successful, non-zero otherwise.
187 static int encodeOid(
188 const unsigned char *inStr
,
189 unsigned char **outOid
,
190 unsigned int *outLen
)
192 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
193 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
195 unsigned numDigitBytes
; /* total #of output chars */
196 unsigned char firstByte
;
198 CFIndex numsToProcess
;
199 CFStringRef oidStr
= NULL
;
200 CFArrayRef argvRef
= NULL
;
204 /* parse input string into array of substrings */
205 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
206 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
207 if (!oidStr
) goto cleanExit
;
208 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
209 if (!argvRef
) goto cleanExit
;
210 argc
= CFArrayGetCount(argvRef
);
211 if (argc
< 3) goto cleanExit
;
213 /* first two numbers in OID munge together */
214 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
215 if (num
< 0) goto cleanExit
;
216 firstByte
= (40 * num
);
217 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
218 if (num
< 0) goto cleanExit
;
222 numsToProcess
= argc
- 2;
223 if(numsToProcess
> 0) {
224 /* skip this loop in the unlikely event that input is only two numbers */
225 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
226 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
227 for(digit
=0; digit
<numsToProcess
; digit
++) {
228 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
229 if (num
< 0) goto cleanExit
;
230 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
231 numDigitBytes
+= numDigits
[digit
];
234 *outLen
= (2 + numDigitBytes
);
235 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
237 *outP
++ = numDigitBytes
;
239 for(digit
=0; digit
<numsToProcess
; digit
++) {
240 unsigned int byteDex
;
241 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
242 *outP
++ = digits
[digit
][byteDex
];
246 for(digit
=0; digit
<numsToProcess
; digit
++) {
253 if (digits
) free(digits
);
254 if (numDigits
) free(numDigits
);
255 if (oidStr
) CFRelease(oidStr
);
256 if (argvRef
) CFRelease(argvRef
);
262 * Given a CF object reference describing an OID, convert to binary DER format
263 * and fill out the CSSM_OID structure provided by the caller. Caller is
264 * responsible for freeing the data pointer in outOid->Data.
266 * Function returns 0 if successful, non-zero otherwise.
269 static int convertOid(
273 if (!inRef
|| !outOid
)
276 unsigned char *oidData
= NULL
;
277 unsigned int oidLen
= 0;
279 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
280 // CFStringRef: OID representation is a dotted-decimal string
281 CFStringRef inStr
= (CFStringRef
)inRef
;
282 CFIndex max
= CFStringGetLength(inStr
) * 3;
284 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
287 if(encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0)
290 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
291 // CFDataRef: OID representation is in binary DER format
292 CFDataRef inData
= (CFDataRef
)inRef
;
293 oidLen
= (unsigned int) CFDataGetLength(inData
);
294 oidData
= (unsigned char *) malloc(oidLen
);
295 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
298 // Not in a format we understand
301 outOid
->Length
= oidLen
;
302 outOid
->Data
= (uint8_t *)oidData
;
306 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
308 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
309 static void cmsEncoderClassInitialize(void)
312 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
315 /* init called out from _CFRuntimeCreateInstance() */
316 static void cmsEncoderInit(CFTypeRef enc
)
318 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
319 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
323 * Dispose of a CMSEncoder. Called out from CFRelease().
325 static void cmsEncoderFinalize(
328 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
329 if(cmsEncoder
== NULL
) {
332 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
333 free(cmsEncoder
->eContentType
.Data
);
335 CFRELEASE(cmsEncoder
->signers
);
336 CFRELEASE(cmsEncoder
->recipients
);
337 CFRELEASE(cmsEncoder
->otherCerts
);
338 if(cmsEncoder
->cmsMsg
!= NULL
) {
339 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
340 cmsEncoder
->cmsMsg
= NULL
;
342 if(cmsEncoder
->encoder
!= NULL
) {
344 * Normally this gets freed in SecCmsEncoderFinish - this is
347 SecCmsEncoderDestroy(cmsEncoder
->encoder
);
351 static OSStatus
cmsSetupEncoder(
352 CMSEncoderRef cmsEncoder
)
356 ASSERT(cmsEncoder
->arena
== NULL
);
357 ASSERT(cmsEncoder
->encoder
== NULL
);
359 cmsEncoder
->encoderOut
= CFDataCreateMutable(NULL
, 0);
360 if (!cmsEncoder
->encoderOut
) {
361 return errSecAllocate
;
364 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
365 NULL
, NULL
, // no callback
366 cmsEncoder
->encoderOut
, // data goes here
367 NULL
, NULL
, // no password callback (right?)
368 NULL
, NULL
, // decrypt key callback
369 &cmsEncoder
->encoder
);
371 return cmsRtnToOSStatus(ortn
);
373 return errSecSuccess
;
377 * Set up a SecCmsMessageRef for a SignedData creation.
379 static OSStatus
cmsSetupForSignedData(
380 CMSEncoderRef cmsEncoder
)
382 ASSERT((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
));
384 SecCmsContentInfoRef contentInfo
= NULL
;
385 SecCmsSignedDataRef signedData
= NULL
;
388 /* build chain of objects: message->signedData->data */
389 if(cmsEncoder
->cmsMsg
!= NULL
) {
390 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
392 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
393 if(cmsEncoder
->cmsMsg
== NULL
) {
394 return errSecInternalComponent
;
397 signedData
= SecCmsSignedDataCreate(cmsEncoder
->cmsMsg
);
398 if(signedData
== NULL
) {
399 return errSecInternalComponent
;
401 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
402 ortn
= SecCmsContentInfoSetContentSignedData(contentInfo
,signedData
);
404 return cmsRtnToOSStatus(ortn
);
406 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
407 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
408 /* Override the default eContentType of id-data */
409 ortn
= SecCmsContentInfoSetContentOther(contentInfo
,
410 NULL
, /* data - provided to encoder, not here */
411 cmsEncoder
->detachedContent
,
412 &cmsEncoder
->eContentType
);
415 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
416 NULL
, /* data - provided to encoder, not here */
417 cmsEncoder
->detachedContent
);
420 ortn
= cmsRtnToOSStatus(ortn
);
421 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
425 /* optional 'global' (per-SignedData) certs */
426 if(cmsEncoder
->otherCerts
!= NULL
) {
427 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
429 ortn
= cmsRtnToOSStatus(ortn
);
430 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
435 /* SignerInfos, one per signer */
436 CFIndex numSigners
= 0;
437 if(cmsEncoder
->signers
!= NULL
) {
438 /* this is optional...in case we're just creating a cert bundle */
439 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
442 SecCertificateRef ourCert
= NULL
;
443 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
445 switch(cmsEncoder
->chainMode
) {
446 case kCMSCertificateNone
:
447 chainMode
= SecCmsCMNone
;
449 case kCMSCertificateSignerOnly
:
450 chainMode
= SecCmsCMCertOnly
;
452 case kCMSCertificateChainWithRoot
:
453 chainMode
= SecCmsCMCertChainWithRoot
;
458 for(dex
=0; dex
<numSigners
; dex
++) {
459 SecCmsSignerInfoRef signerInfo
;
461 SecIdentityRef ourId
=
462 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
463 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
465 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
468 /* this creates the signerInfo and adds it to the signedData object */
469 signerInfo
= SecCmsSignerInfoCreate(signedData
, ourId
, cmsEncoder
->digestalgtag
);
470 if (signerInfo
== NULL
) {
471 ortn
= errSecInternalComponent
;
475 /* we want the cert chain included for this one */
476 /* NOTE the usage parameter is currently unused by the SMIME lib */
477 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
478 certUsageEmailSigner
);
480 ortn
= cmsRtnToOSStatus(ortn
);
481 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
486 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
487 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
489 ortn
= cmsRtnToOSStatus(ortn
);
490 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
494 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
495 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
497 ortn
= cmsRtnToOSStatus(ortn
);
498 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
502 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
503 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
505 ortn
= cmsRtnToOSStatus(ortn
);
506 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
510 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
511 if (cmsEncoder
->signingTime
== 0)
512 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
513 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
515 ortn
= cmsRtnToOSStatus(ortn
);
516 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
520 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgility
) {
521 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo
, cmsEncoder
->hashAgilityAttrValue
);
522 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
523 CFReleaseNull(cmsEncoder
->hashAgilityAttrValue
);
525 ortn
= cmsRtnToOSStatus(ortn
);
526 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn
);
530 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgilityV2
) {
531 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(signerInfo
, cmsEncoder
->hashAgilityV2AttrValues
);
532 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
533 CFReleaseNull(cmsEncoder
->hashAgilityV2AttrValues
);
535 ortn
= cmsRtnToOSStatus(ortn
);
536 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgilityV2", ortn
);
551 * Set up a SecCmsMessageRef for a EnvelopedData creation.
553 static OSStatus
cmsSetupForEnvelopedData(
554 CMSEncoderRef cmsEncoder
)
556 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
557 ASSERT(cmsEncoder
->recipients
!= NULL
);
559 SecCmsContentInfoRef contentInfo
= NULL
;
560 SecCmsEnvelopedDataRef envelopedData
= NULL
;
561 SECOidTag algorithmTag
;
566 * Find encryption algorithm...unfortunately we need a NULL-terminated array
567 * of SecCertificateRefs for this.
569 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
571 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
572 (numCerts
+1) * sizeof(SecCertificateRef
));
574 for(dex
=0; dex
<numCerts
; dex
++) {
575 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
576 cmsEncoder
->recipients
, dex
);
578 certArray
[numCerts
] = NULL
;
579 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
582 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
586 /* build chain of objects: message->envelopedData->data */
587 if(cmsEncoder
->cmsMsg
!= NULL
) {
588 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
590 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
591 if(cmsEncoder
->cmsMsg
== NULL
) {
592 return errSecInternalComponent
;
594 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
595 algorithmTag
, keySize
);
596 if(envelopedData
== NULL
) {
597 return errSecInternalComponent
;
599 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
600 ortn
= SecCmsContentInfoSetContentEnvelopedData(contentInfo
, envelopedData
);
602 ortn
= cmsRtnToOSStatus(ortn
);
603 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
606 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
607 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
608 /* Override the default ContentType of id-data */
609 ortn
= SecCmsContentInfoSetContentOther(contentInfo
,
610 NULL
, /* data - provided to encoder, not here */
611 FALSE
, /* detachedContent */
612 &cmsEncoder
->eContentType
);
615 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
616 NULL
/* data - provided to encoder, not here */,
617 cmsEncoder
->detachedContent
);
620 ortn
= cmsRtnToOSStatus(ortn
);
621 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
626 * create & attach recipient information, one for each recipient
628 for(dex
=0; dex
<numCerts
; dex
++) {
629 SecCmsRecipientInfoRef recipientInfo
= NULL
;
631 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
632 cmsEncoder
->recipients
, dex
);
633 recipientInfo
= SecCmsRecipientInfoCreate(envelopedData
, thisRecip
);
634 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
636 ortn
= cmsRtnToOSStatus(ortn
);
637 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
641 return errSecSuccess
;
645 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
646 * from CMSEncodeGetCmsMessage().
648 static OSStatus
cmsSetupCmsMsg(
649 CMSEncoderRef cmsEncoder
)
651 ASSERT(cmsEncoder
!= NULL
);
652 ASSERT(cmsEncoder
->encState
== ES_Init
);
654 /* figure out what high-level operation we're doing */
655 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
656 if(cmsEncoder
->recipients
!= NULL
) {
657 cmsEncoder
->op
= EO_SignEncrypt
;
660 cmsEncoder
->op
= EO_Sign
;
663 else if(cmsEncoder
->recipients
!= NULL
) {
664 cmsEncoder
->op
= EO_Encrypt
;
667 dprintf("CMSEncoderUpdateContent: nothing to do\n");
671 OSStatus ortn
= errSecSuccess
;
673 switch(cmsEncoder
->op
) {
676 /* If we're signing & encrypting, do the signing first */
677 ortn
= cmsSetupForSignedData(cmsEncoder
);
680 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
683 cmsEncoder
->encState
= ES_Msg
;
688 * ASN.1 template for decoding a ContentInfo.
691 SecAsn1Oid contentType
;
695 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
696 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
697 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
698 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
699 offsetof(SimpleContentInfo
, content
),
700 kSecAsn1AnyTemplate
},
705 * Obtain the content of a contentInfo, This basically strips off the contentType OID
706 * and returns its ASN_ANY content, allocated the provided coder's memory space.
708 static OSStatus
cmsContentInfoContent(
709 SecAsn1CoderRef asn1Coder
,
710 const SecAsn1Item
*contentInfo
,
711 SecAsn1Item
*content
) /* RETURNED */
714 SimpleContentInfo decodedInfo
;
716 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
717 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
718 cmsSimpleContentInfoTemplate
, &decodedInfo
);
722 if(decodedInfo
.content
.Data
== NULL
) {
723 dprintf("***Error decoding contentInfo: no content\n");
724 return errSecInternalComponent
;
726 *content
= decodedInfo
.content
;
727 return errSecSuccess
;
730 #pragma mark --- Start of Public API ---
732 CFTypeID
CMSEncoderGetTypeID(void)
734 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
736 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
737 pthread_once(&once
, &cmsEncoderClassInitialize
);
739 return cmsEncoderTypeID
;
743 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
745 OSStatus
CMSEncoderCreate(
746 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
748 CMSEncoderRef cmsEncoder
= NULL
;
750 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
751 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
753 if(cmsEncoder
== NULL
) {
754 return errSecAllocate
;
756 cmsEncoder
->encState
= ES_Init
;
757 cmsEncoder
->chainMode
= kCMSCertificateChain
;
758 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
759 *cmsEncoderOut
= cmsEncoder
;
760 return errSecSuccess
;
763 #pragma mark --- Getters & Setters ---
765 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1
= CFSTR("sha1");
766 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256
= CFSTR("sha256");
769 OSStatus
CMSEncoderSetSignerAlgorithm(
770 CMSEncoderRef cmsEncoder
,
771 CFStringRef digestAlgorithm
)
773 if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA1
)) {
774 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
775 } else if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA256
)) {
776 cmsEncoder
->digestalgtag
= SEC_OID_SHA256
;
781 return errSecSuccess
;
785 * Specify signers of the CMS message; implies that the message will be signed.
787 OSStatus
CMSEncoderAddSigners(
788 CMSEncoderRef cmsEncoder
,
789 CFTypeRef signerOrArray
)
791 if(cmsEncoder
== NULL
) {
794 if(cmsEncoder
->encState
!= ES_Init
) {
797 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
801 * Obtain an array of signers as specified in CMSEncoderSetSigners().
803 OSStatus
CMSEncoderCopySigners(
804 CMSEncoderRef cmsEncoder
,
807 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
810 if(cmsEncoder
->signers
!= NULL
) {
811 CFRetain(cmsEncoder
->signers
);
813 *signers
= cmsEncoder
->signers
;
814 return errSecSuccess
;
818 * Specify recipients of the message. Implies that the message will be encrypted.
820 OSStatus
CMSEncoderAddRecipients(
821 CMSEncoderRef cmsEncoder
,
822 CFTypeRef recipientOrArray
)
824 if(cmsEncoder
== NULL
) {
827 if(cmsEncoder
->encState
!= ES_Init
) {
830 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
831 SecCertificateGetTypeID());
835 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
837 OSStatus
CMSEncoderCopyRecipients(
838 CMSEncoderRef cmsEncoder
,
839 CFArrayRef
*recipients
)
841 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
844 if(cmsEncoder
->recipients
!= NULL
) {
845 CFRetain(cmsEncoder
->recipients
);
847 *recipients
= cmsEncoder
->recipients
;
848 return errSecSuccess
;
852 * Specify additional certs to include in a signed message.
854 OSStatus
CMSEncoderAddSupportingCerts(
855 CMSEncoderRef cmsEncoder
,
856 CFTypeRef certOrArray
)
858 if(cmsEncoder
== NULL
) {
861 if(cmsEncoder
->encState
!= ES_Init
) {
864 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
865 SecCertificateGetTypeID());
869 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
871 OSStatus
CMSEncoderCopySupportingCerts(
872 CMSEncoderRef cmsEncoder
,
873 CFArrayRef
*certs
) /* RETURNED */
875 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
878 if(cmsEncoder
->otherCerts
!= NULL
) {
879 CFRetain(cmsEncoder
->otherCerts
);
881 *certs
= cmsEncoder
->otherCerts
;
882 return errSecSuccess
;
885 OSStatus
CMSEncoderSetHasDetachedContent(
886 CMSEncoderRef cmsEncoder
,
887 Boolean detachedContent
)
889 if(cmsEncoder
== NULL
) {
892 if(cmsEncoder
->encState
!= ES_Init
) {
895 cmsEncoder
->detachedContent
= detachedContent
;
896 return errSecSuccess
;
899 OSStatus
CMSEncoderGetHasDetachedContent(
900 CMSEncoderRef cmsEncoder
,
901 Boolean
*detachedContent
) /* RETURNED */
903 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
906 *detachedContent
= cmsEncoder
->detachedContent
;
907 return errSecSuccess
;
911 * Optionally specify an eContentType OID for the inner EncapsulatedData for
912 * a signed message. The default eContentType, used of this function is not
913 * called, is id-data.
915 static OSStatus
CMSEncoderSetEncapsulatedContentType(
916 CMSEncoderRef cmsEncoder
,
917 const SecAsn1Oid
*eContentType
)
919 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
922 if(cmsEncoder
->encState
!= ES_Init
) {
926 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
927 if(ecOid
->Data
!= NULL
) {
930 cmsCopyCmsData(eContentType
, ecOid
);
931 return errSecSuccess
;
934 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
935 CMSEncoderRef cmsEncoder
,
936 CFTypeRef eContentTypeOID
)
938 // convert eContentTypeOID to a CSSM_OID
939 SecAsn1Oid contentType
= { 0, NULL
};
940 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
942 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
943 if (contentType
.Data
)
944 free(contentType
.Data
);
949 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
951 OSStatus
CMSEncoderCopyEncapsulatedContentType(
952 CMSEncoderRef cmsEncoder
,
953 CFDataRef
*eContentType
)
955 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
959 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
960 if(ecOid
->Data
== NULL
) {
961 *eContentType
= NULL
;
964 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
966 return errSecSuccess
;
970 * Optionally specify signed attributes. Only meaningful when creating a
971 * signed message. If this is called, it must be called before
972 * CMSEncoderUpdateContent().
974 OSStatus
CMSEncoderAddSignedAttributes(
975 CMSEncoderRef cmsEncoder
,
976 CMSSignedAttributes signedAttributes
)
978 if(cmsEncoder
== NULL
) {
981 if(cmsEncoder
->encState
!= ES_Init
) {
984 cmsEncoder
->signedAttributes
|= signedAttributes
;
985 return errSecSuccess
;
989 * Set the signing time for a CMSEncoder.
990 * This is only used if the kCMSAttrSigningTime attribute is included.
992 OSStatus
CMSEncoderSetSigningTime(
993 CMSEncoderRef cmsEncoder
,
996 if(cmsEncoder
== NULL
) {
999 if(cmsEncoder
->encState
!= ES_Init
) {
1002 cmsEncoder
->signingTime
= time
;
1003 return errSecSuccess
;
1007 * Set the hash agility attribute for a CMSEncoder.
1008 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute
1011 OSStatus
CMSEncoderSetAppleCodesigningHashAgility(
1012 CMSEncoderRef cmsEncoder
,
1013 CFDataRef hashAgilityAttrValue
)
1015 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1018 cmsEncoder
->hashAgilityAttrValue
= CFRetainSafe(hashAgilityAttrValue
);
1019 return errSecSuccess
;
1023 * Set the hash agility attribute for a CMSEncoder.
1024 * This is only used if the kCMSAttrAppleCodesigningHashAgilityV2 attribute
1027 OSStatus
CMSEncoderSetAppleCodesigningHashAgilityV2(
1028 CMSEncoderRef cmsEncoder
,
1029 CFDictionaryRef hashAgilityV2AttrValues
)
1031 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1034 cmsEncoder
->hashAgilityV2AttrValues
= CFRetainSafe(hashAgilityV2AttrValues
);
1035 return errSecSuccess
;
1038 OSStatus
CMSEncoderSetCertificateChainMode(
1039 CMSEncoderRef cmsEncoder
,
1040 CMSCertificateChainMode chainMode
)
1042 if(cmsEncoder
== NULL
) {
1045 if(cmsEncoder
->encState
!= ES_Init
) {
1049 case kCMSCertificateNone
:
1050 case kCMSCertificateSignerOnly
:
1051 case kCMSCertificateChain
:
1052 case kCMSCertificateChainWithRoot
:
1057 cmsEncoder
->chainMode
= chainMode
;
1058 return errSecSuccess
;
1061 OSStatus
CMSEncoderGetCertificateChainMode(
1062 CMSEncoderRef cmsEncoder
,
1063 CMSCertificateChainMode
*chainModeOut
)
1065 if(cmsEncoder
== NULL
) {
1068 *chainModeOut
= cmsEncoder
->chainMode
;
1069 return errSecSuccess
;
1072 #if TIMESTAMPING_SUPPORTED
1074 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1076 if (cmsEncoder
->cmsMsg
)
1077 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1081 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1083 if (cmsEncoder
->cmsMsg
)
1084 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1088 #pragma mark --- Action ---
1091 * Feed content bytes into the encoder.
1092 * Can be called multiple times.
1093 * No 'setter' routines can be called after this function has been called.
1095 OSStatus
CMSEncoderUpdateContent(
1096 CMSEncoderRef cmsEncoder
,
1097 const void *content
,
1100 if(cmsEncoder
== NULL
) {
1104 OSStatus ortn
= errSecSuccess
;
1105 switch(cmsEncoder
->encState
) {
1108 * First time thru: do the CmsMsg setup.
1110 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1114 /* fall thru to set up the encoder */
1117 /* We have a cmsMsg but no encoder; create one */
1118 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1119 ASSERT(cmsEncoder
->encoder
== NULL
);
1120 ortn
= cmsSetupEncoder(cmsEncoder
);
1124 /* only legal calls now are update and finalize */
1125 cmsEncoder
->encState
= ES_Updating
;
1129 ASSERT(cmsEncoder
->encoder
!= NULL
);
1133 /* Too late for another update */
1137 return errSecInternalComponent
;
1140 /* FIXME - CFIndex same size as size_t on 64bit? */
1141 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1143 ortn
= cmsRtnToOSStatus(ortn
);
1144 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1149 /* forward declaration */
1150 static OSStatus
CMSEncode(
1152 CFTypeRef recipients
,
1153 const SecAsn1Oid
*eContentType
,
1154 Boolean detachedContent
,
1155 CMSSignedAttributes signedAttributes
,
1156 const void *content
,
1158 CFDataRef
*encodedContent
);
1161 * Finish encoding the message and obtain the encoded result.
1162 * Caller must CFRelease the result.
1164 OSStatus
CMSEncoderCopyEncodedContent(
1165 CMSEncoderRef cmsEncoder
,
1166 CFDataRef
*encodedContent
)
1168 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1174 switch(cmsEncoder
->encState
) {
1176 /* normal termination */
1179 /* already been called */
1184 * The only time these are legal is when we're doing a SignedData
1185 * with certificates only (no signers, no content).
1187 if((cmsEncoder
->signers
!= NULL
) ||
1188 (cmsEncoder
->recipients
!= NULL
) ||
1189 (cmsEncoder
->otherCerts
== NULL
)) {
1193 /* Set up for certs only */
1194 ortn
= cmsSetupForSignedData(cmsEncoder
);
1198 /* and an encoder */
1199 ortn
= cmsSetupEncoder(cmsEncoder
);
1207 ASSERT(cmsEncoder
->encoder
!= NULL
);
1208 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1209 /* regardless of the outcome, the encoder itself has been freed */
1210 cmsEncoder
->encoder
= NULL
;
1212 return cmsRtnToOSStatus(ortn
);
1214 cmsEncoder
->encState
= ES_Final
;
1216 if((cmsEncoder
->encoderOut
== NULL
) && !cmsEncoder
->customCoder
) {
1217 /* not sure how this could happen... */
1218 dprintf("Successful encode, but no data\n");
1219 return errSecInternalComponent
;
1221 if(cmsEncoder
->customCoder
) {
1223 *encodedContent
= NULL
;
1224 return errSecSuccess
;
1227 /* in two out of three cases, we're done */
1228 switch(cmsEncoder
->op
) {
1231 *encodedContent
= CFDataCreateCopy(NULL
, cmsEncoder
->encoderOut
);
1232 return errSecSuccess
;
1233 case EO_SignEncrypt
:
1234 /* proceed, more work to do */
1239 * Signing & encrypting.
1240 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1241 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1242 * inside of the ContentInfo we just created as the data to encrypt.
1244 SecAsn1CoderRef asn1Coder
= NULL
;
1245 SecAsn1Item signedData
= {0, NULL
};
1247 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1251 SecAsn1Item encoderOut
= { CFDataGetLength(cmsEncoder
->encoderOut
),
1252 CFDataGetMutableBytePtr(cmsEncoder
->encoderOut
)};
1253 ortn
= cmsContentInfoContent(asn1Coder
, &encoderOut
, &signedData
);
1258 /* now just encrypt that, one-shot */
1259 ortn
= CMSEncode(NULL
, /* no signers this time */
1260 cmsEncoder
->recipients
,
1261 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1262 * encode the signedData - this asserts the
1263 * SEC_OID_OTHER OID tag in the EnvelopedData's
1265 FALSE
, /* detachedContent */
1266 kCMSAttrNone
, /* signedAttributes - none this time */
1267 signedData
.Data
, signedData
.Length
,
1272 SecAsn1CoderRelease(asn1Coder
);
1277 #pragma mark --- High-level API ---
1280 * High-level, one-shot encoder function.
1282 static OSStatus
CMSEncode(
1284 CFTypeRef recipients
,
1285 const SecAsn1Oid
*eContentType
,
1286 Boolean detachedContent
,
1287 CMSSignedAttributes signedAttributes
,
1288 const void *content
,
1290 CFDataRef
*encodedContent
) /* RETURNED */
1292 if((signers
== NULL
) && (recipients
== NULL
)) {
1295 if(encodedContent
== NULL
) {
1299 CMSEncoderRef cmsEncoder
;
1302 /* set up the encoder */
1303 ortn
= CMSEncoderCreate(&cmsEncoder
);
1308 /* subsequent errors to errOut: */
1310 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1316 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1322 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1327 if(detachedContent
) {
1328 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1333 if(signedAttributes
) {
1334 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1340 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1344 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1347 CFRelease(cmsEncoder
);
1351 OSStatus
CMSEncodeContent(
1353 CFTypeRef recipients
,
1354 CFTypeRef eContentTypeOID
,
1355 Boolean detachedContent
,
1356 CMSSignedAttributes signedAttributes
,
1357 const void *content
,
1359 CFDataRef
*encodedContentOut
) /* RETURNED */
1361 // convert eContentTypeOID to a CSSM_OID
1362 SecAsn1Oid contentType
= { 0, NULL
};
1363 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1365 const SecAsn1Oid
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1366 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1367 detachedContent
, signedAttributes
,
1368 content
, contentLen
, encodedContentOut
);
1369 if (contentType
.Data
)
1370 free(contentType
.Data
);
1374 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1377 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1378 * If we don't have a SecCmsMessageRef yet, we create one now.
1379 * This is the only place where we go to state ES_Msg.
1381 OSStatus
CMSEncoderGetCmsMessage(
1382 CMSEncoderRef cmsEncoder
,
1383 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1385 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1388 if(cmsEncoder
->cmsMsg
!= NULL
) {
1389 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1390 *cmsMessage
= cmsEncoder
->cmsMsg
;
1391 return errSecSuccess
;
1394 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1398 *cmsMessage
= cmsEncoder
->cmsMsg
;
1400 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1401 cmsEncoder
->encState
= ES_Msg
;
1402 return errSecSuccess
;
1406 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1407 * If this is called, it must be called before the first call to
1408 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1409 * incoming SecCmsEncoderRef.
1411 OSStatus
CMSEncoderSetEncoder(
1412 CMSEncoderRef cmsEncoder
,
1413 SecCmsEncoderRef encoder
)
1415 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1421 switch(cmsEncoder
->encState
) {
1423 /* No message, no encoder */
1424 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1425 ASSERT(cmsEncoder
->encoder
== NULL
);
1426 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1430 /* drop thru to set encoder */
1432 /* cmsMsg but no encoder */
1433 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1434 ASSERT(cmsEncoder
->encoder
== NULL
);
1435 cmsEncoder
->encoder
= encoder
;
1436 cmsEncoder
->encState
= ES_Updating
;
1437 cmsEncoder
->customCoder
= true; /* we won't see data */
1438 return errSecSuccess
;
1440 /* no can do, too late */
1446 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1447 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1448 * CMSEncoderUpdateContent() has been called.
1449 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1451 OSStatus
CMSEncoderGetEncoder(
1452 CMSEncoderRef cmsEncoder
,
1453 SecCmsEncoderRef
*encoder
) /* RETURNED */
1455 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1459 /* any state, whether we have an encoder or not is OK */
1460 *encoder
= cmsEncoder
->encoder
;
1461 return errSecSuccess
;
1464 #if TIMESTAMPING_SUPPORTED
1465 #include <AssertMacros.h>
1467 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1468 * present. This timestamp is an authenticated timestamp provided by
1469 * a timestamping authority.
1471 * Returns errSecParam if the CMS message was not signed or if signerIndex
1472 * is greater than the number of signers of the message minus one.
1474 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1476 OSStatus
CMSEncoderCopySignerTimestamp(
1477 CMSEncoderRef cmsEncoder
,
1478 size_t signerIndex
, /* usually 0 */
1479 CFAbsoluteTime
*timestamp
) /* RETURNED */
1481 return CMSEncoderCopySignerTimestampWithPolicy(
1488 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1489 CMSEncoderRef cmsEncoder
,
1490 CFTypeRef timeStampPolicy
,
1491 size_t signerIndex
, /* usually 0 */
1492 CFAbsoluteTime
*timestamp
) /* RETURNED */
1494 OSStatus status
= errSecParam
;
1495 SecCmsMessageRef cmsg
;
1496 SecCmsSignedDataRef signedData
= NULL
;
1497 int numContentInfos
= 0;
1499 require(cmsEncoder
&& timestamp
, xit
);
1500 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1501 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1502 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1504 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1505 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1506 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1507 if ((signedData
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(ci
))) {
1508 SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
);
1511 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);