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 <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/SecSMIME.h>
42 #include <Security/oidsattr.h>
43 #include <Security/SecAsn1Coder.h>
44 #include <Security/SecAsn1Types.h>
45 #include <Security/SecAsn1Templates.h>
46 #include <CoreFoundation/CFRuntime.h>
48 #include <utilities/SecCFRelease.h>
50 #include <security_smime/cmspriv.h>
52 #if TIMESTAMPING_SUPPORTED
53 #include <security_smime/tsaSupport.h>
56 #pragma mark --- Private types and definitions ---
62 ES_Init
, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent
63 * and CMSEncodeGetCmsMessage */
64 ES_Msg
, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */
65 ES_Updating
, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */
66 ES_Final
/* CMSEncoderCopyEncodedContent has been called */
70 * High-level operation: what are we doing?
79 * Caller's CMSEncoderRef points to one of these.
83 CMSEncoderState encState
;
85 Boolean detachedContent
;
86 SecAsn1Oid eContentType
;
87 CFMutableArrayRef signers
;
88 CFMutableArrayRef recipients
;
89 CFMutableArrayRef otherCerts
;
90 CMSSignedAttributes signedAttributes
;
91 CFAbsoluteTime signingTime
;
92 SecCmsMessageRef cmsMsg
; /* the encoder's arena */
93 SecCmsEncoderRef encoder
;
94 CFMutableDataRef encoderOut
; /* output goes here... */
95 bool customCoder
; /* unless this is set by
96 * CMSEncoderSetEncoder */
97 SECOidTag digestalgtag
;
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 */
121 #if TIMESTAMPING_SUPPORTED
122 void CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
125 #pragma mark --- Private routines ---
128 * Decode a CFStringRef representation of an integer
130 static int cfStringToNumber(
135 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
141 * Encode an integer component of an OID, return resulting number of bytes;
142 * actual bytes are mallocd and returned in *encodeArray.
144 static unsigned encodeNumber(
146 unsigned char **encodeArray
) // mallocd and RETURNED
148 unsigned char *result
;
150 unsigned numDigits
= 0;
153 /* trival case - 0 maps to 0 */
155 *encodeArray
= (unsigned char *)malloc(1);
160 /* first calculate the number of digits in num, base 128 */
161 scratch
= (unsigned)num
;
162 while(scratch
!= 0) {
167 result
= (unsigned char *)malloc(numDigits
);
168 scratch
= (unsigned)num
;
169 for(dex
=0; dex
<numDigits
; dex
++) {
170 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
174 /* all digits except the last one have m.s. bit set */
175 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
179 *encodeArray
= result
;
184 * Given an OID in dotted-decimal string representation, convert to binary
185 * DER format. Returns a pointer in outOid which the caller must free(),
186 * as well as the length of the data in outLen.
187 * Function returns 0 if successful, non-zero otherwise.
189 static int encodeOid(
190 const unsigned char *inStr
,
191 unsigned char **outOid
,
192 unsigned int *outLen
)
194 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
195 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
197 unsigned numDigitBytes
; /* total #of output chars */
198 unsigned char firstByte
;
200 CFIndex numsToProcess
;
201 CFStringRef oidStr
= NULL
;
202 CFArrayRef argvRef
= NULL
;
206 /* parse input string into array of substrings */
207 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
208 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
209 if (!oidStr
) goto cleanExit
;
210 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
211 if (!argvRef
) goto cleanExit
;
212 argc
= CFArrayGetCount(argvRef
);
213 if (argc
< 3) goto cleanExit
;
215 /* first two numbers in OID munge together */
216 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
217 if (num
< 0) goto cleanExit
;
218 firstByte
= (40 * num
);
219 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
220 if (num
< 0) goto cleanExit
;
224 numsToProcess
= argc
- 2;
225 if(numsToProcess
> 0) {
226 /* skip this loop in the unlikely event that input is only two numbers */
227 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
228 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
229 for(digit
=0; digit
<numsToProcess
; digit
++) {
230 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
231 if (num
< 0) goto cleanExit
;
232 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
233 numDigitBytes
+= numDigits
[digit
];
236 *outLen
= (2 + numDigitBytes
);
237 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
239 *outP
++ = numDigitBytes
;
241 for(digit
=0; digit
<numsToProcess
; digit
++) {
242 unsigned int byteDex
;
243 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
244 *outP
++ = digits
[digit
][byteDex
];
248 for(digit
=0; digit
<numsToProcess
; digit
++) {
255 if (digits
) free(digits
);
256 if (numDigits
) free(numDigits
);
257 if (oidStr
) CFRelease(oidStr
);
258 if (argvRef
) CFRelease(argvRef
);
264 * Given a CF object reference describing an OID, convert to binary DER format
265 * and fill out the CSSM_OID structure provided by the caller. Caller is
266 * responsible for freeing the data pointer in outOid->Data.
268 * Function returns 0 if successful, non-zero otherwise.
271 static int convertOid(
275 if (!inRef
|| !outOid
)
278 unsigned char *oidData
= NULL
;
279 unsigned int oidLen
= 0;
281 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
282 // CFStringRef: OID representation is a dotted-decimal string
283 CFStringRef inStr
= (CFStringRef
)inRef
;
284 CFIndex max
= CFStringGetLength(inStr
) * 3;
285 char *buf
= (char *)malloc(max
);
287 return errSecMemoryError
;
289 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
)) {
294 if (encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0) {
300 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
301 // CFDataRef: OID representation is in binary DER format
302 CFDataRef inData
= (CFDataRef
)inRef
;
303 oidLen
= (unsigned int) CFDataGetLength(inData
);
304 oidData
= (unsigned char *) malloc(oidLen
);
305 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
308 // Not in a format we understand
311 outOid
->Length
= oidLen
;
312 outOid
->Data
= (uint8_t *)oidData
;
316 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
318 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
319 static void cmsEncoderClassInitialize(void)
322 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
325 /* init called out from _CFRuntimeCreateInstance() */
326 static void cmsEncoderInit(CFTypeRef enc
)
328 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
329 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
333 * Dispose of a CMSEncoder. Called out from CFRelease().
335 static void cmsEncoderFinalize(
338 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
339 if(cmsEncoder
== NULL
) {
342 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
343 free(cmsEncoder
->eContentType
.Data
);
345 CFRELEASE(cmsEncoder
->signers
);
346 CFRELEASE(cmsEncoder
->recipients
);
347 CFRELEASE(cmsEncoder
->otherCerts
);
348 if(cmsEncoder
->cmsMsg
!= NULL
) {
349 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
350 cmsEncoder
->cmsMsg
= NULL
;
352 if(cmsEncoder
->encoder
!= NULL
) {
354 * Normally this gets freed in SecCmsEncoderFinish - this is
357 SecCmsEncoderDestroy(cmsEncoder
->encoder
);
361 static OSStatus
cmsSetupEncoder(
362 CMSEncoderRef cmsEncoder
)
366 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
367 ASSERT(cmsEncoder
->encoder
== NULL
);
369 cmsEncoder
->encoderOut
= CFDataCreateMutable(NULL
, 0);
370 if (!cmsEncoder
->encoderOut
) {
371 return errSecAllocate
;
374 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
375 NULL
, NULL
, // no callback
376 cmsEncoder
->encoderOut
, // data goes here
377 NULL
, NULL
, // no password callback (right?)
378 NULL
, NULL
, // decrypt key callback
379 &cmsEncoder
->encoder
);
381 return cmsRtnToOSStatus(ortn
);
383 return errSecSuccess
;
387 * Set up a SecCmsMessageRef for a SignedData creation.
389 static OSStatus
cmsSetupForSignedData(
390 CMSEncoderRef cmsEncoder
)
392 ASSERT((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
));
394 SecCmsContentInfoRef contentInfo
= NULL
;
395 SecCmsSignedDataRef signedData
= NULL
;
398 /* build chain of objects: message->signedData->data */
399 if(cmsEncoder
->cmsMsg
!= NULL
) {
400 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
402 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
403 if(cmsEncoder
->cmsMsg
== NULL
) {
404 return errSecInternalComponent
;
407 signedData
= SecCmsSignedDataCreate(cmsEncoder
->cmsMsg
);
408 if(signedData
== NULL
) {
409 return errSecInternalComponent
;
411 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
412 ortn
= SecCmsContentInfoSetContentSignedData(contentInfo
,signedData
);
414 return cmsRtnToOSStatus(ortn
);
416 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
417 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
418 /* Override the default eContentType of id-data */
419 ortn
= SecCmsContentInfoSetContentOther(contentInfo
,
420 NULL
, /* data - provided to encoder, not here */
421 cmsEncoder
->detachedContent
,
422 &cmsEncoder
->eContentType
);
425 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
426 NULL
, /* data - provided to encoder, not here */
427 cmsEncoder
->detachedContent
);
430 ortn
= cmsRtnToOSStatus(ortn
);
431 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
435 /* optional 'global' (per-SignedData) certs */
436 if(cmsEncoder
->otherCerts
!= NULL
) {
437 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
439 ortn
= cmsRtnToOSStatus(ortn
);
440 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
445 /* SignerInfos, one per signer */
446 CFIndex numSigners
= 0;
447 if(cmsEncoder
->signers
!= NULL
) {
448 /* this is optional...in case we're just creating a cert bundle */
449 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
452 SecCertificateRef ourCert
= NULL
;
453 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
455 switch(cmsEncoder
->chainMode
) {
456 case kCMSCertificateNone
:
457 chainMode
= SecCmsCMNone
;
459 case kCMSCertificateSignerOnly
:
460 chainMode
= SecCmsCMCertOnly
;
462 case kCMSCertificateChainWithRoot
:
463 chainMode
= SecCmsCMCertChainWithRoot
;
465 case kCMSCertificateChainWithRootOrFail
:
466 chainMode
= SecCmsCMCertChainWithRootOrFail
;
471 for(dex
=0; dex
<numSigners
; dex
++) {
472 SecCmsSignerInfoRef signerInfo
;
474 SecIdentityRef ourId
=
475 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
476 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
478 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
481 /* this creates the signerInfo and adds it to the signedData object */
482 signerInfo
= SecCmsSignerInfoCreate(signedData
, ourId
, cmsEncoder
->digestalgtag
);
483 if (signerInfo
== NULL
) {
484 ortn
= errSecInternalComponent
;
488 /* we want the cert chain included for this one */
489 /* NOTE the usage parameter is currently unused by the SMIME lib */
490 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
491 certUsageEmailSigner
);
493 ortn
= cmsRtnToOSStatus(ortn
);
494 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
499 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
500 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
502 ortn
= cmsRtnToOSStatus(ortn
);
503 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
507 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
508 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
510 ortn
= cmsRtnToOSStatus(ortn
);
511 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
515 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
516 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
518 ortn
= cmsRtnToOSStatus(ortn
);
519 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
523 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
524 if (cmsEncoder
->signingTime
== 0)
525 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
526 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
528 ortn
= cmsRtnToOSStatus(ortn
);
529 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
533 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgility
) {
534 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo
, cmsEncoder
->hashAgilityAttrValue
);
535 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
536 CFReleaseNull(cmsEncoder
->hashAgilityAttrValue
);
538 ortn
= cmsRtnToOSStatus(ortn
);
539 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn
);
543 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgilityV2
) {
544 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(signerInfo
, cmsEncoder
->hashAgilityV2AttrValues
);
545 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
546 CFReleaseNull(cmsEncoder
->hashAgilityV2AttrValues
);
548 ortn
= cmsRtnToOSStatus(ortn
);
549 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgilityV2", ortn
);
553 if (cmsEncoder
->signedAttributes
& kCMSAttrAppleExpirationTime
) {
554 ortn
= SecCmsSignerInfoAddAppleExpirationTime(signerInfo
, cmsEncoder
->expirationTime
);
556 ortn
= cmsRtnToOSStatus(ortn
);
557 CSSM_PERROR("SecCmsSignerInfoAddAppleExpirationTime", ortn
);
572 * Set up a SecCmsMessageRef for a EnvelopedData creation.
574 static OSStatus
cmsSetupForEnvelopedData(
575 CMSEncoderRef cmsEncoder
)
577 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
578 ASSERT(cmsEncoder
->recipients
!= NULL
);
580 SecCmsContentInfoRef contentInfo
= NULL
;
581 SecCmsEnvelopedDataRef envelopedData
= NULL
;
582 SECOidTag algorithmTag
;
587 * Find encryption algorithm...unfortunately we need a NULL-terminated array
588 * of SecCertificateRefs for this.
590 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
592 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
593 (numCerts
+1) * sizeof(SecCertificateRef
));
595 for(dex
=0; dex
<numCerts
; dex
++) {
596 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
597 cmsEncoder
->recipients
, dex
);
599 certArray
[numCerts
] = NULL
;
600 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
603 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
607 /* build chain of objects: message->envelopedData->data */
608 if(cmsEncoder
->cmsMsg
!= NULL
) {
609 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
611 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
612 if(cmsEncoder
->cmsMsg
== NULL
) {
613 return errSecInternalComponent
;
615 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
616 algorithmTag
, keySize
);
617 if(envelopedData
== NULL
) {
618 return errSecInternalComponent
;
620 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
621 ortn
= SecCmsContentInfoSetContentEnvelopedData(contentInfo
, envelopedData
);
623 ortn
= cmsRtnToOSStatus(ortn
);
624 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
627 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
628 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
629 /* Override the default ContentType of id-data */
630 ortn
= SecCmsContentInfoSetContentOther(contentInfo
,
631 NULL
, /* data - provided to encoder, not here */
632 FALSE
, /* detachedContent */
633 &cmsEncoder
->eContentType
);
636 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
637 NULL
/* data - provided to encoder, not here */,
638 cmsEncoder
->detachedContent
);
641 ortn
= cmsRtnToOSStatus(ortn
);
642 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
647 * create & attach recipient information, one for each recipient
649 for(dex
=0; dex
<numCerts
; dex
++) {
650 SecCmsRecipientInfoRef recipientInfo
= NULL
;
652 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
653 cmsEncoder
->recipients
, dex
);
654 recipientInfo
= SecCmsRecipientInfoCreate(envelopedData
, thisRecip
);
655 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
657 ortn
= cmsRtnToOSStatus(ortn
);
658 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
662 return errSecSuccess
;
666 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
667 * from CMSEncodeGetCmsMessage().
669 static OSStatus
cmsSetupCmsMsg(
670 CMSEncoderRef cmsEncoder
)
672 ASSERT(cmsEncoder
!= NULL
);
673 ASSERT(cmsEncoder
->encState
== ES_Init
);
675 /* figure out what high-level operation we're doing */
676 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
677 if(cmsEncoder
->recipients
!= NULL
) {
678 cmsEncoder
->op
= EO_SignEncrypt
;
681 cmsEncoder
->op
= EO_Sign
;
684 else if(cmsEncoder
->recipients
!= NULL
) {
685 cmsEncoder
->op
= EO_Encrypt
;
688 dprintf("CMSEncoderUpdateContent: nothing to do\n");
692 OSStatus ortn
= errSecSuccess
;
694 switch(cmsEncoder
->op
) {
697 /* If we're signing & encrypting, do the signing first */
698 ortn
= cmsSetupForSignedData(cmsEncoder
);
701 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
704 cmsEncoder
->encState
= ES_Msg
;
709 * ASN.1 template for decoding a ContentInfo.
712 SecAsn1Oid contentType
;
716 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
717 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
718 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
719 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
720 offsetof(SimpleContentInfo
, content
),
721 kSecAsn1AnyTemplate
},
726 * Obtain the content of a contentInfo, This basically strips off the contentType OID
727 * and returns its ASN_ANY content, allocated the provided coder's memory space.
729 static OSStatus
cmsContentInfoContent(
730 SecAsn1CoderRef asn1Coder
,
731 const SecAsn1Item
*contentInfo
,
732 SecAsn1Item
*content
) /* RETURNED */
735 SimpleContentInfo decodedInfo
;
737 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
738 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
739 cmsSimpleContentInfoTemplate
, &decodedInfo
);
743 if(decodedInfo
.content
.Data
== NULL
) {
744 dprintf("***Error decoding contentInfo: no content\n");
745 return errSecInternalComponent
;
747 *content
= decodedInfo
.content
;
748 return errSecSuccess
;
751 #pragma mark --- Start of Public API ---
753 CFTypeID
CMSEncoderGetTypeID(void)
755 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
757 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
758 pthread_once(&once
, &cmsEncoderClassInitialize
);
760 return cmsEncoderTypeID
;
764 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
766 OSStatus
CMSEncoderCreate(
767 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
769 CMSEncoderRef cmsEncoder
= NULL
;
771 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
772 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
774 if(cmsEncoder
== NULL
) {
775 return errSecAllocate
;
777 cmsEncoder
->encState
= ES_Init
;
778 cmsEncoder
->chainMode
= kCMSCertificateChain
;
779 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
780 *cmsEncoderOut
= cmsEncoder
;
781 return errSecSuccess
;
784 #pragma mark --- Getters & Setters ---
786 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1
= CFSTR("sha1");
787 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256
= CFSTR("sha256");
790 OSStatus
CMSEncoderSetSignerAlgorithm(
791 CMSEncoderRef cmsEncoder
,
792 CFStringRef digestAlgorithm
)
794 if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA1
)) {
795 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
796 } else if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA256
)) {
797 cmsEncoder
->digestalgtag
= SEC_OID_SHA256
;
802 return errSecSuccess
;
806 * Specify signers of the CMS message; implies that the message will be signed.
808 OSStatus
CMSEncoderAddSigners(
809 CMSEncoderRef cmsEncoder
,
810 CFTypeRef signerOrArray
)
812 if(cmsEncoder
== NULL
) {
815 if(cmsEncoder
->encState
!= ES_Init
) {
818 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
822 * Obtain an array of signers as specified in CMSEncoderSetSigners().
824 OSStatus
CMSEncoderCopySigners(
825 CMSEncoderRef cmsEncoder
,
828 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
831 if(cmsEncoder
->signers
!= NULL
) {
832 CFRetain(cmsEncoder
->signers
);
834 *signers
= cmsEncoder
->signers
;
835 return errSecSuccess
;
839 * Specify recipients of the message. Implies that the message will be encrypted.
841 OSStatus
CMSEncoderAddRecipients(
842 CMSEncoderRef cmsEncoder
,
843 CFTypeRef recipientOrArray
)
845 if(cmsEncoder
== NULL
) {
848 if(cmsEncoder
->encState
!= ES_Init
) {
851 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
852 SecCertificateGetTypeID());
856 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
858 OSStatus
CMSEncoderCopyRecipients(
859 CMSEncoderRef cmsEncoder
,
860 CFArrayRef
*recipients
)
862 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
865 if(cmsEncoder
->recipients
!= NULL
) {
866 CFRetain(cmsEncoder
->recipients
);
868 *recipients
= cmsEncoder
->recipients
;
869 return errSecSuccess
;
873 * Specify additional certs to include in a signed message.
875 OSStatus
CMSEncoderAddSupportingCerts(
876 CMSEncoderRef cmsEncoder
,
877 CFTypeRef certOrArray
)
879 if(cmsEncoder
== NULL
) {
882 if(cmsEncoder
->encState
!= ES_Init
) {
885 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
886 SecCertificateGetTypeID());
890 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
892 OSStatus
CMSEncoderCopySupportingCerts(
893 CMSEncoderRef cmsEncoder
,
894 CFArrayRef
*certs
) /* RETURNED */
896 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
899 if(cmsEncoder
->otherCerts
!= NULL
) {
900 CFRetain(cmsEncoder
->otherCerts
);
902 *certs
= cmsEncoder
->otherCerts
;
903 return errSecSuccess
;
906 OSStatus
CMSEncoderSetHasDetachedContent(
907 CMSEncoderRef cmsEncoder
,
908 Boolean detachedContent
)
910 if(cmsEncoder
== NULL
) {
913 if(cmsEncoder
->encState
!= ES_Init
) {
916 cmsEncoder
->detachedContent
= detachedContent
;
917 return errSecSuccess
;
920 OSStatus
CMSEncoderGetHasDetachedContent(
921 CMSEncoderRef cmsEncoder
,
922 Boolean
*detachedContent
) /* RETURNED */
924 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
927 *detachedContent
= cmsEncoder
->detachedContent
;
928 return errSecSuccess
;
932 * Optionally specify an eContentType OID for the inner EncapsulatedData for
933 * a signed message. The default eContentType, used of this function is not
934 * called, is id-data.
936 static OSStatus
CMSEncoderSetEncapsulatedContentType(
937 CMSEncoderRef cmsEncoder
,
938 const SecAsn1Oid
*eContentType
)
940 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
943 if(cmsEncoder
->encState
!= ES_Init
) {
947 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
948 if(ecOid
->Data
!= NULL
) {
951 cmsCopyCmsData(eContentType
, ecOid
);
952 return errSecSuccess
;
955 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
956 CMSEncoderRef cmsEncoder
,
957 CFTypeRef eContentTypeOID
)
959 // convert eContentTypeOID to a CSSM_OID
960 SecAsn1Oid contentType
= { 0, NULL
};
961 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
963 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
964 if (contentType
.Data
)
965 free(contentType
.Data
);
970 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
972 OSStatus
CMSEncoderCopyEncapsulatedContentType(
973 CMSEncoderRef cmsEncoder
,
974 CFDataRef
*eContentType
)
976 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
980 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
981 if(ecOid
->Data
== NULL
) {
982 *eContentType
= NULL
;
985 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
987 return errSecSuccess
;
991 * Optionally specify signed attributes. Only meaningful when creating a
992 * signed message. If this is called, it must be called before
993 * CMSEncoderUpdateContent().
995 OSStatus
CMSEncoderAddSignedAttributes(
996 CMSEncoderRef cmsEncoder
,
997 CMSSignedAttributes signedAttributes
)
999 if(cmsEncoder
== NULL
) {
1002 if(cmsEncoder
->encState
!= ES_Init
) {
1005 cmsEncoder
->signedAttributes
|= signedAttributes
;
1006 return errSecSuccess
;
1010 * Set the signing time for a CMSEncoder.
1011 * This is only used if the kCMSAttrSigningTime attribute is included.
1013 OSStatus
CMSEncoderSetSigningTime(
1014 CMSEncoderRef cmsEncoder
,
1015 CFAbsoluteTime time
)
1017 if(cmsEncoder
== NULL
) {
1020 if(cmsEncoder
->encState
!= ES_Init
) {
1023 cmsEncoder
->signingTime
= time
;
1024 return errSecSuccess
;
1028 * Set the hash agility attribute for a CMSEncoder.
1029 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute
1032 OSStatus
CMSEncoderSetAppleCodesigningHashAgility(
1033 CMSEncoderRef cmsEncoder
,
1034 CFDataRef hashAgilityAttrValue
)
1036 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1039 cmsEncoder
->hashAgilityAttrValue
= CFRetainSafe(hashAgilityAttrValue
);
1040 return errSecSuccess
;
1044 * Set the hash agility attribute for a CMSEncoder.
1045 * This is only used if the kCMSAttrAppleCodesigningHashAgilityV2 attribute
1048 OSStatus
CMSEncoderSetAppleCodesigningHashAgilityV2(
1049 CMSEncoderRef cmsEncoder
,
1050 CFDictionaryRef hashAgilityV2AttrValues
)
1052 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1055 cmsEncoder
->hashAgilityV2AttrValues
= CFRetainSafe(hashAgilityV2AttrValues
);
1056 return errSecSuccess
;
1060 * Set the expiration time for a CMSEncoder.
1061 * This is only used if the kCMSAttrAppleExpirationTime attribute is included.
1063 OSStatus
CMSEncoderSetAppleExpirationTime(
1064 CMSEncoderRef cmsEncoder
,
1065 CFAbsoluteTime time
)
1067 if(cmsEncoder
== NULL
) {
1070 if(cmsEncoder
->encState
!= ES_Init
) {
1073 cmsEncoder
->expirationTime
= time
;
1074 return errSecSuccess
;
1077 OSStatus
CMSEncoderSetCertificateChainMode(
1078 CMSEncoderRef cmsEncoder
,
1079 CMSCertificateChainMode chainMode
)
1081 if(cmsEncoder
== NULL
) {
1084 if(cmsEncoder
->encState
!= ES_Init
) {
1088 case kCMSCertificateNone
:
1089 case kCMSCertificateSignerOnly
:
1090 case kCMSCertificateChain
:
1091 case kCMSCertificateChainWithRoot
:
1092 case kCMSCertificateChainWithRootOrFail
:
1097 cmsEncoder
->chainMode
= chainMode
;
1098 return errSecSuccess
;
1101 OSStatus
CMSEncoderGetCertificateChainMode(
1102 CMSEncoderRef cmsEncoder
,
1103 CMSCertificateChainMode
*chainModeOut
)
1105 if(cmsEncoder
== NULL
) {
1108 *chainModeOut
= cmsEncoder
->chainMode
;
1109 return errSecSuccess
;
1112 #if TIMESTAMPING_SUPPORTED
1114 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1116 if (cmsEncoder
->cmsMsg
)
1117 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1121 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1123 if (cmsEncoder
->cmsMsg
)
1124 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1128 #pragma mark --- Action ---
1131 * Feed content bytes into the encoder.
1132 * Can be called multiple times.
1133 * No 'setter' routines can be called after this function has been called.
1135 OSStatus
CMSEncoderUpdateContent(
1136 CMSEncoderRef cmsEncoder
,
1137 const void *content
,
1140 if(cmsEncoder
== NULL
) {
1144 OSStatus ortn
= errSecSuccess
;
1145 switch(cmsEncoder
->encState
) {
1148 * First time thru: do the CmsMsg setup.
1150 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1154 /* fall thru to set up the encoder */
1157 /* We have a cmsMsg but no encoder; create one */
1158 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1159 ASSERT(cmsEncoder
->encoder
== NULL
);
1160 ortn
= cmsSetupEncoder(cmsEncoder
);
1164 /* only legal calls now are update and finalize */
1165 cmsEncoder
->encState
= ES_Updating
;
1169 ASSERT(cmsEncoder
->encoder
!= NULL
);
1173 /* Too late for another update */
1177 return errSecInternalComponent
;
1180 /* FIXME - CFIndex same size as size_t on 64bit? */
1181 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1183 ortn
= cmsRtnToOSStatus(ortn
);
1184 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1189 /* forward declaration */
1190 static OSStatus
CMSEncode(
1192 CFTypeRef recipients
,
1193 const SecAsn1Oid
*eContentType
,
1194 Boolean detachedContent
,
1195 CMSSignedAttributes signedAttributes
,
1196 const void *content
,
1198 CFDataRef
*encodedContent
);
1201 * Finish encoding the message and obtain the encoded result.
1202 * Caller must CFRelease the result.
1204 OSStatus
CMSEncoderCopyEncodedContent(
1205 CMSEncoderRef cmsEncoder
,
1206 CFDataRef
*encodedContent
)
1208 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1214 switch(cmsEncoder
->encState
) {
1216 /* normal termination */
1219 /* already been called */
1224 * The only time these are legal is when we're doing a SignedData
1225 * with certificates only (no signers, no content).
1227 if((cmsEncoder
->signers
!= NULL
) ||
1228 (cmsEncoder
->recipients
!= NULL
) ||
1229 (cmsEncoder
->otherCerts
== NULL
)) {
1233 /* Set up for certs only */
1234 ortn
= cmsSetupForSignedData(cmsEncoder
);
1238 /* and an encoder */
1239 ortn
= cmsSetupEncoder(cmsEncoder
);
1247 ASSERT(cmsEncoder
->encoder
!= NULL
);
1248 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1249 /* regardless of the outcome, the encoder itself has been freed */
1250 cmsEncoder
->encoder
= NULL
;
1252 return cmsRtnToOSStatus(ortn
);
1254 cmsEncoder
->encState
= ES_Final
;
1256 if((cmsEncoder
->encoderOut
== NULL
) && !cmsEncoder
->customCoder
) {
1257 /* not sure how this could happen... */
1258 dprintf("Successful encode, but no data\n");
1259 return errSecInternalComponent
;
1261 if(cmsEncoder
->customCoder
) {
1263 *encodedContent
= NULL
;
1264 return errSecSuccess
;
1267 /* in two out of three cases, we're done */
1268 switch(cmsEncoder
->op
) {
1271 *encodedContent
= CFDataCreateCopy(NULL
, cmsEncoder
->encoderOut
);
1272 return errSecSuccess
;
1273 case EO_SignEncrypt
:
1274 /* proceed, more work to do */
1279 * Signing & encrypting.
1280 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1281 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1282 * inside of the ContentInfo we just created as the data to encrypt.
1284 SecAsn1CoderRef asn1Coder
= NULL
;
1285 SecAsn1Item signedData
= {0, NULL
};
1287 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1291 SecAsn1Item encoderOut
= { CFDataGetLength(cmsEncoder
->encoderOut
),
1292 CFDataGetMutableBytePtr(cmsEncoder
->encoderOut
)};
1293 ortn
= cmsContentInfoContent(asn1Coder
, &encoderOut
, &signedData
);
1298 /* now just encrypt that, one-shot */
1299 ortn
= CMSEncode(NULL
, /* no signers this time */
1300 cmsEncoder
->recipients
,
1301 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1302 * encode the signedData - this asserts the
1303 * SEC_OID_OTHER OID tag in the EnvelopedData's
1305 FALSE
, /* detachedContent */
1306 kCMSAttrNone
, /* signedAttributes - none this time */
1307 signedData
.Data
, signedData
.Length
,
1312 SecAsn1CoderRelease(asn1Coder
);
1317 #pragma mark --- High-level API ---
1320 * High-level, one-shot encoder function.
1322 static OSStatus
CMSEncode(
1324 CFTypeRef recipients
,
1325 const SecAsn1Oid
*eContentType
,
1326 Boolean detachedContent
,
1327 CMSSignedAttributes signedAttributes
,
1328 const void *content
,
1330 CFDataRef
*encodedContent
) /* RETURNED */
1332 if((signers
== NULL
) && (recipients
== NULL
)) {
1335 if(encodedContent
== NULL
) {
1339 CMSEncoderRef cmsEncoder
;
1342 /* set up the encoder */
1343 ortn
= CMSEncoderCreate(&cmsEncoder
);
1348 /* subsequent errors to errOut: */
1350 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1356 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1362 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1367 if(detachedContent
) {
1368 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1373 if(signedAttributes
) {
1374 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1380 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1384 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1387 CFRelease(cmsEncoder
);
1391 OSStatus
CMSEncodeContent(
1393 CFTypeRef recipients
,
1394 CFTypeRef eContentTypeOID
,
1395 Boolean detachedContent
,
1396 CMSSignedAttributes signedAttributes
,
1397 const void *content
,
1399 CFDataRef
*encodedContentOut
) /* RETURNED */
1401 // convert eContentTypeOID to a CSSM_OID
1402 SecAsn1Oid contentType
= { 0, NULL
};
1403 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1405 const SecAsn1Oid
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1406 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1407 detachedContent
, signedAttributes
,
1408 content
, contentLen
, encodedContentOut
);
1409 if (contentType
.Data
)
1410 free(contentType
.Data
);
1414 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1417 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1418 * If we don't have a SecCmsMessageRef yet, we create one now.
1419 * This is the only place where we go to state ES_Msg.
1421 OSStatus
CMSEncoderGetCmsMessage(
1422 CMSEncoderRef cmsEncoder
,
1423 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1425 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1428 if(cmsEncoder
->cmsMsg
!= NULL
) {
1429 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1430 *cmsMessage
= cmsEncoder
->cmsMsg
;
1431 return errSecSuccess
;
1434 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1438 *cmsMessage
= cmsEncoder
->cmsMsg
;
1440 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1441 cmsEncoder
->encState
= ES_Msg
;
1442 return errSecSuccess
;
1446 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1447 * If this is called, it must be called before the first call to
1448 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1449 * incoming SecCmsEncoderRef.
1451 OSStatus
CMSEncoderSetEncoder(
1452 CMSEncoderRef cmsEncoder
,
1453 SecCmsEncoderRef encoder
)
1455 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1461 switch(cmsEncoder
->encState
) {
1463 /* No message, no encoder */
1464 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1465 ASSERT(cmsEncoder
->encoder
== NULL
);
1466 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1470 /* drop thru to set encoder */
1472 /* cmsMsg but no encoder */
1473 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1474 ASSERT(cmsEncoder
->encoder
== NULL
);
1475 cmsEncoder
->encoder
= encoder
;
1476 cmsEncoder
->encState
= ES_Updating
;
1477 cmsEncoder
->customCoder
= true; /* we won't see data */
1478 return errSecSuccess
;
1480 /* no can do, too late */
1486 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1487 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1488 * CMSEncoderUpdateContent() has been called.
1489 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1491 OSStatus
CMSEncoderGetEncoder(
1492 CMSEncoderRef cmsEncoder
,
1493 SecCmsEncoderRef
*encoder
) /* RETURNED */
1495 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1499 /* any state, whether we have an encoder or not is OK */
1500 *encoder
= cmsEncoder
->encoder
;
1501 return errSecSuccess
;
1504 #if TIMESTAMPING_SUPPORTED
1505 #include <AssertMacros.h>
1507 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1508 * present. This timestamp is an authenticated timestamp provided by
1509 * a timestamping authority.
1511 * Returns errSecParam if the CMS message was not signed or if signerIndex
1512 * is greater than the number of signers of the message minus one.
1514 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1516 OSStatus
CMSEncoderCopySignerTimestamp(
1517 CMSEncoderRef cmsEncoder
,
1518 size_t signerIndex
, /* usually 0 */
1519 CFAbsoluteTime
*timestamp
) /* RETURNED */
1521 return CMSEncoderCopySignerTimestampWithPolicy(
1528 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1529 CMSEncoderRef cmsEncoder
,
1530 CFTypeRef timeStampPolicy
,
1531 size_t signerIndex
, /* usually 0 */
1532 CFAbsoluteTime
*timestamp
) /* RETURNED */
1534 OSStatus status
= errSecParam
;
1535 SecCmsMessageRef cmsg
;
1536 SecCmsSignedDataRef signedData
= NULL
;
1537 int numContentInfos
= 0;
1539 require(cmsEncoder
&& timestamp
, xit
);
1540 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1541 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1542 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1544 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1545 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1546 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1547 if ((signedData
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(ci
))) {
1548 SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
);
1551 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);