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 "CMSEncoder.h"
29 #include "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>
51 #include <security_smime/tsaSupport.h>
52 #include <security_smime/cmspriv.h>
54 #pragma mark --- Private types and definitions ---
60 ES_Init
, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent
61 * and CMSEncodeGetCmsMessage */
62 ES_Msg
, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */
63 ES_Updating
, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */
64 ES_Final
/* CMSEncoderCopyEncodedContent has been called */
68 * High-level operation: what are we doing?
77 * Caller's CMSEncoderRef points to one of these.
81 CMSEncoderState encState
;
83 Boolean detachedContent
;
84 CSSM_OID eContentType
;
85 CFMutableArrayRef signers
;
86 CFMutableArrayRef recipients
;
87 CFMutableArrayRef otherCerts
;
88 CMSSignedAttributes signedAttributes
;
89 CFAbsoluteTime signingTime
;
90 SecCmsMessageRef cmsMsg
;
91 SecArenaPoolRef arena
; /* the encoder's arena */
92 SecCmsEncoderRef encoder
;
93 CSSM_DATA encoderOut
; /* output goes here... */
94 bool customCoder
; /* unless this is set by
95 * CMSEncoderSetEncoder */
96 CMSCertificateChainMode chainMode
;
99 static void cmsEncoderInit(CFTypeRef enc
);
100 static void cmsEncoderFinalize(CFTypeRef enc
);
102 static CFRuntimeClass cmsEncoderRuntimeClass
=
109 NULL
, /* equal - just use pointer equality */
110 NULL
, /* hash, ditto */
111 NULL
, /* copyFormattingDesc */
112 NULL
/* copyDebugDesc */
116 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
118 #pragma mark --- Private routines ---
121 * Decode a CFStringRef representation of an integer
123 static int cfStringToNumber(
128 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
134 * Encode an integer component of an OID, return resulting number of bytes;
135 * actual bytes are mallocd and returned in *encodeArray.
137 static unsigned encodeNumber(
139 unsigned char **encodeArray
) // mallocd and RETURNED
141 unsigned char *result
;
143 unsigned numDigits
= 0;
146 /* trival case - 0 maps to 0 */
148 *encodeArray
= (unsigned char *)malloc(1);
153 /* first calculate the number of digits in num, base 128 */
154 scratch
= (unsigned)num
;
155 while(scratch
!= 0) {
160 result
= (unsigned char *)malloc(numDigits
);
161 scratch
= (unsigned)num
;
162 for(dex
=0; dex
<numDigits
; dex
++) {
163 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
167 /* all digits except the last one have m.s. bit set */
168 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
172 *encodeArray
= result
;
177 * Given an OID in dotted-decimal string representation, convert to binary
178 * DER format. Returns a pointer in outOid which the caller must free(),
179 * as well as the length of the data in outLen.
180 * Function returns 0 if successful, non-zero otherwise.
182 static int encodeOid(
183 const unsigned char *inStr
,
184 unsigned char **outOid
,
185 unsigned int *outLen
)
187 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
188 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
190 unsigned numDigitBytes
; /* total #of output chars */
191 unsigned char firstByte
;
193 CFIndex numsToProcess
;
194 CFStringRef oidStr
= NULL
;
195 CFArrayRef argvRef
= NULL
;
199 /* parse input string into array of substrings */
200 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
201 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
202 if (!oidStr
) goto cleanExit
;
203 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
204 if (!argvRef
) goto cleanExit
;
205 argc
= CFArrayGetCount(argvRef
);
206 if (argc
< 3) goto cleanExit
;
208 /* first two numbers in OID munge together */
209 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
210 if (num
< 0) goto cleanExit
;
211 firstByte
= (40 * num
);
212 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
213 if (num
< 0) goto cleanExit
;
217 numsToProcess
= argc
- 2;
218 if(numsToProcess
> 0) {
219 /* skip this loop in the unlikely event that input is only two numbers */
220 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
221 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
222 for(digit
=0; digit
<numsToProcess
; digit
++) {
223 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
224 if (num
< 0) goto cleanExit
;
225 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
226 numDigitBytes
+= numDigits
[digit
];
229 *outLen
= (2 + numDigitBytes
);
230 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
232 *outP
++ = numDigitBytes
;
234 for(digit
=0; digit
<numsToProcess
; digit
++) {
235 unsigned int byteDex
;
236 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
237 *outP
++ = digits
[digit
][byteDex
];
241 for(digit
=0; digit
<numsToProcess
; digit
++) {
250 if (oidStr
) CFRelease(oidStr
);
251 if (argvRef
) CFRelease(argvRef
);
257 * Given a CF object reference describing an OID, convert to binary DER format
258 * and fill out the CSSM_OID structure provided by the caller. Caller is
259 * responsible for freeing the data pointer in outOid->Data.
261 * Function returns 0 if successful, non-zero otherwise.
264 static int convertOid(
268 if (!inRef
|| !outOid
)
271 unsigned char *oidData
= NULL
;
272 unsigned int oidLen
= 0;
274 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
275 // CFStringRef: OID representation is a dotted-decimal string
276 CFStringRef inStr
= (CFStringRef
)inRef
;
277 CFIndex max
= CFStringGetLength(inStr
) * 3;
279 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
282 if(encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0)
285 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
286 // CFDataRef: OID representation is in binary DER format
287 CFDataRef inData
= (CFDataRef
)inRef
;
288 oidLen
= (unsigned int) CFDataGetLength(inData
);
289 oidData
= (unsigned char *) malloc(oidLen
);
290 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
293 // Not in a format we understand
296 outOid
->Length
= oidLen
;
297 outOid
->Data
= (uint8
*)oidData
;
301 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
303 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
304 static void cmsEncoderClassInitialize(void)
307 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
310 /* init called out from _CFRuntimeCreateInstance() */
311 static void cmsEncoderInit(CFTypeRef enc
)
313 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
314 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
318 * Dispose of a CMSEncoder. Called out from CFRelease().
320 static void cmsEncoderFinalize(
323 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
324 if(cmsEncoder
== NULL
) {
327 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
328 free(cmsEncoder
->eContentType
.Data
);
330 CFRELEASE(cmsEncoder
->signers
);
331 CFRELEASE(cmsEncoder
->recipients
);
332 CFRELEASE(cmsEncoder
->otherCerts
);
333 if(cmsEncoder
->cmsMsg
!= NULL
) {
334 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
336 if(cmsEncoder
->arena
!= NULL
) {
337 SecArenaPoolFree(cmsEncoder
->arena
, false);
339 if(cmsEncoder
->encoder
!= NULL
) {
341 * Normally this gets freed in SecCmsEncoderFinish - this is
344 SecCmsEncoderDestroy(cmsEncoder
->encoder
);
348 static OSStatus
cmsSetupEncoder(
349 CMSEncoderRef cmsEncoder
)
353 ASSERT(cmsEncoder
->arena
== NULL
);
354 ASSERT(cmsEncoder
->encoder
== NULL
);
356 ortn
= SecArenaPoolCreate(1024, &cmsEncoder
->arena
);
358 return cmsRtnToOSStatus(ortn
);
360 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
361 NULL
, NULL
, // no callback
362 &cmsEncoder
->encoderOut
, // data goes here
364 NULL
, NULL
, // no password callback (right?)
365 NULL
, NULL
, // decrypt key callback
366 NULL
, NULL
, // detached digests
367 &cmsEncoder
->encoder
);
369 return cmsRtnToOSStatus(ortn
);
371 return errSecSuccess
;
375 * Set up a SecCmsMessageRef for a SignedData creation.
377 static OSStatus
cmsSetupForSignedData(
378 CMSEncoderRef cmsEncoder
)
380 ASSERT((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
));
382 SecCmsContentInfoRef contentInfo
= NULL
;
383 SecCmsSignedDataRef signedData
= NULL
;
386 /* build chain of objects: message->signedData->data */
387 if(cmsEncoder
->cmsMsg
!= NULL
) {
388 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
390 cmsEncoder
->cmsMsg
= SecCmsMessageCreate(NULL
);
391 if(cmsEncoder
->cmsMsg
== NULL
) {
392 return errSecInternalComponent
;
395 signedData
= SecCmsSignedDataCreate(cmsEncoder
->cmsMsg
);
396 if(signedData
== NULL
) {
397 return errSecInternalComponent
;
399 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
400 ortn
= SecCmsContentInfoSetContentSignedData(cmsEncoder
->cmsMsg
, contentInfo
,
403 return cmsRtnToOSStatus(ortn
);
405 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
406 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
407 /* Override the default eContentType of id-data */
408 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
410 NULL
, /* data - provided to encoder, not here */
411 cmsEncoder
->detachedContent
,
412 &cmsEncoder
->eContentType
);
415 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
417 NULL
, /* data - provided to encoder, not here */
418 cmsEncoder
->detachedContent
);
421 ortn
= cmsRtnToOSStatus(ortn
);
422 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
426 /* optional 'global' (per-SignedData) certs */
427 if(cmsEncoder
->otherCerts
!= NULL
) {
428 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
430 ortn
= cmsRtnToOSStatus(ortn
);
431 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
436 /* SignerInfos, one per signer */
437 CFIndex numSigners
= 0;
438 if(cmsEncoder
->signers
!= NULL
) {
439 /* this is optional...in case we're just creating a cert bundle */
440 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
443 SecKeychainRef ourKc
= NULL
;
444 SecCertificateRef ourCert
= NULL
;
445 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
447 switch(cmsEncoder
->chainMode
) {
448 case kCMSCertificateNone
:
449 chainMode
= SecCmsCMNone
;
451 case kCMSCertificateSignerOnly
:
452 chainMode
= SecCmsCMCertOnly
;
454 case kCMSCertificateChainWithRoot
:
455 chainMode
= SecCmsCMCertChainWithRoot
;
460 for(dex
=0; dex
<numSigners
; dex
++) {
461 SecCmsSignerInfoRef signerInfo
;
463 SecIdentityRef ourId
=
464 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
465 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
467 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
470 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)ourCert
, &ourKc
);
472 CSSM_PERROR("SecKeychainItemCopyKeychain", ortn
);
475 signerInfo
= SecCmsSignerInfoCreate(cmsEncoder
->cmsMsg
, ourId
, SEC_OID_SHA1
);
476 if (signerInfo
== NULL
) {
477 ortn
= errSecInternalComponent
;
481 /* we want the cert chain included for this one */
482 /* NOTE the usage parameter is currently unused by the SMIME lib */
483 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
484 certUsageEmailSigner
);
486 ortn
= cmsRtnToOSStatus(ortn
);
487 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
492 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
493 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
495 ortn
= cmsRtnToOSStatus(ortn
);
496 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
500 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
501 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, ourKc
);
503 ortn
= cmsRtnToOSStatus(ortn
);
504 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
508 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
509 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, ourKc
);
511 ortn
= cmsRtnToOSStatus(ortn
);
512 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
516 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
517 if (cmsEncoder
->signingTime
== 0)
518 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
519 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
521 ortn
= cmsRtnToOSStatus(ortn
);
522 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
527 ortn
= SecCmsSignedDataAddSignerInfo(signedData
, signerInfo
);
529 ortn
= cmsRtnToOSStatus(ortn
);
530 CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn
);
547 * Set up a SecCmsMessageRef for a EnvelopedData creation.
549 static OSStatus
cmsSetupForEnvelopedData(
550 CMSEncoderRef cmsEncoder
)
552 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
553 ASSERT(cmsEncoder
->recipients
!= NULL
);
555 SecCmsContentInfoRef contentInfo
= NULL
;
556 SecCmsEnvelopedDataRef envelopedData
= NULL
;
557 SECOidTag algorithmTag
;
562 * Find encryption algorithm...unfortunately we need a NULL-terminated array
563 * of SecCertificateRefs for this.
565 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
567 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
568 (numCerts
+1) * sizeof(SecCertificateRef
));
570 for(dex
=0; dex
<numCerts
; dex
++) {
571 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
572 cmsEncoder
->recipients
, dex
);
574 certArray
[numCerts
] = NULL
;
575 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
578 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
582 /* build chain of objects: message->envelopedData->data */
583 if(cmsEncoder
->cmsMsg
!= NULL
) {
584 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
586 cmsEncoder
->cmsMsg
= SecCmsMessageCreate(NULL
);
587 if(cmsEncoder
->cmsMsg
== NULL
) {
588 return errSecInternalComponent
;
590 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
591 algorithmTag
, keySize
);
592 if(envelopedData
== NULL
) {
593 return errSecInternalComponent
;
595 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
596 ortn
= SecCmsContentInfoSetContentEnvelopedData(cmsEncoder
->cmsMsg
,
597 contentInfo
, envelopedData
);
599 ortn
= cmsRtnToOSStatus(ortn
);
600 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
603 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
604 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
605 /* Override the default ContentType of id-data */
606 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
608 NULL
, /* data - provided to encoder, not here */
609 FALSE
, /* detachedContent */
610 &cmsEncoder
->eContentType
);
613 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
615 NULL
/* data - provided to encoder, not here */,
616 cmsEncoder
->detachedContent
);
619 ortn
= cmsRtnToOSStatus(ortn
);
620 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
625 * create & attach recipient information, one for each recipient
627 for(dex
=0; dex
<numCerts
; dex
++) {
628 SecCmsRecipientInfoRef recipientInfo
= NULL
;
630 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
631 cmsEncoder
->recipients
, dex
);
632 recipientInfo
= SecCmsRecipientInfoCreate(cmsEncoder
->cmsMsg
, thisRecip
);
633 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
635 ortn
= cmsRtnToOSStatus(ortn
);
636 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
640 return errSecSuccess
;
644 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
645 * from CMSEncodeGetCmsMessage().
647 static OSStatus
cmsSetupCmsMsg(
648 CMSEncoderRef cmsEncoder
)
650 ASSERT(cmsEncoder
!= NULL
);
651 ASSERT(cmsEncoder
->encState
== ES_Init
);
653 /* figure out what high-level operation we're doing */
654 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
655 if(cmsEncoder
->recipients
!= NULL
) {
656 cmsEncoder
->op
= EO_SignEncrypt
;
659 cmsEncoder
->op
= EO_Sign
;
662 else if(cmsEncoder
->recipients
!= NULL
) {
663 cmsEncoder
->op
= EO_Encrypt
;
666 dprintf("CMSEncoderUpdateContent: nothing to do\n");
670 OSStatus ortn
= errSecSuccess
;
672 switch(cmsEncoder
->op
) {
675 /* If we're signing & encrypting, do the signing first */
676 ortn
= cmsSetupForSignedData(cmsEncoder
);
679 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
682 cmsEncoder
->encState
= ES_Msg
;
687 * ASN.1 template for decoding a ContentInfo.
690 CSSM_OID contentType
;
694 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
695 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
696 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
697 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
698 offsetof(SimpleContentInfo
, content
),
699 kSecAsn1AnyTemplate
},
704 * Obtain the content of a contentInfo, This basically strips off the contentType OID
705 * and returns its ASN_ANY content, allocated the provided coder's memory space.
707 static OSStatus
cmsContentInfoContent(
708 SecAsn1CoderRef asn1Coder
,
709 const CSSM_DATA
*contentInfo
,
710 CSSM_DATA
*content
) /* RETURNED */
713 SimpleContentInfo decodedInfo
;
715 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
716 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
717 cmsSimpleContentInfoTemplate
, &decodedInfo
);
721 if(decodedInfo
.content
.Data
== NULL
) {
722 dprintf("***Error decoding contentInfo: no content\n");
723 return errSecInternalComponent
;
725 *content
= decodedInfo
.content
;
726 return errSecSuccess
;
729 #pragma mark --- Start of Public API ---
731 CFTypeID
CMSEncoderGetTypeID(void)
733 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
735 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
736 pthread_once(&once
, &cmsEncoderClassInitialize
);
738 return cmsEncoderTypeID
;
742 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
744 OSStatus
CMSEncoderCreate(
745 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
747 CMSEncoderRef cmsEncoder
= NULL
;
749 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
750 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
752 if(cmsEncoder
== NULL
) {
753 return errSecAllocate
;
755 cmsEncoder
->encState
= ES_Init
;
756 cmsEncoder
->chainMode
= kCMSCertificateChain
;
757 *cmsEncoderOut
= cmsEncoder
;
758 return errSecSuccess
;
761 #pragma mark --- Getters & Setters ---
764 * Specify signers of the CMS message; implies that the message will be signed.
766 OSStatus
CMSEncoderAddSigners(
767 CMSEncoderRef cmsEncoder
,
768 CFTypeRef signerOrArray
)
770 if(cmsEncoder
== NULL
) {
773 if(cmsEncoder
->encState
!= ES_Init
) {
776 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
780 * Obtain an array of signers as specified in CMSEncoderSetSigners().
782 OSStatus
CMSEncoderCopySigners(
783 CMSEncoderRef cmsEncoder
,
786 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
789 if(cmsEncoder
->signers
!= NULL
) {
790 CFRetain(cmsEncoder
->signers
);
792 *signers
= cmsEncoder
->signers
;
793 return errSecSuccess
;
797 * Specify recipients of the message. Implies that the message will be encrypted.
799 OSStatus
CMSEncoderAddRecipients(
800 CMSEncoderRef cmsEncoder
,
801 CFTypeRef recipientOrArray
)
803 if(cmsEncoder
== NULL
) {
806 if(cmsEncoder
->encState
!= ES_Init
) {
809 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
810 SecCertificateGetTypeID());
814 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
816 OSStatus
CMSEncoderCopyRecipients(
817 CMSEncoderRef cmsEncoder
,
818 CFArrayRef
*recipients
)
820 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
823 if(cmsEncoder
->recipients
!= NULL
) {
824 CFRetain(cmsEncoder
->recipients
);
826 *recipients
= cmsEncoder
->recipients
;
827 return errSecSuccess
;
831 * Specify additional certs to include in a signed message.
833 OSStatus
CMSEncoderAddSupportingCerts(
834 CMSEncoderRef cmsEncoder
,
835 CFTypeRef certOrArray
)
837 if(cmsEncoder
== NULL
) {
840 if(cmsEncoder
->encState
!= ES_Init
) {
843 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
844 SecCertificateGetTypeID());
848 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
850 OSStatus
CMSEncoderCopySupportingCerts(
851 CMSEncoderRef cmsEncoder
,
852 CFArrayRef
*certs
) /* RETURNED */
854 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
857 if(cmsEncoder
->otherCerts
!= NULL
) {
858 CFRetain(cmsEncoder
->otherCerts
);
860 *certs
= cmsEncoder
->otherCerts
;
861 return errSecSuccess
;
864 OSStatus
CMSEncoderSetHasDetachedContent(
865 CMSEncoderRef cmsEncoder
,
866 Boolean detachedContent
)
868 if(cmsEncoder
== NULL
) {
871 if(cmsEncoder
->encState
!= ES_Init
) {
874 cmsEncoder
->detachedContent
= detachedContent
;
875 return errSecSuccess
;
878 OSStatus
CMSEncoderGetHasDetachedContent(
879 CMSEncoderRef cmsEncoder
,
880 Boolean
*detachedContent
) /* RETURNED */
882 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
885 *detachedContent
= cmsEncoder
->detachedContent
;
886 return errSecSuccess
;
890 * Optionally specify an eContentType OID for the inner EncapsulatedData for
891 * a signed message. The default eContentType, used of this function is not
892 * called, is id-data.
894 OSStatus
CMSEncoderSetEncapsulatedContentType(
895 CMSEncoderRef cmsEncoder
,
896 const CSSM_OID
*eContentType
)
898 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
901 if(cmsEncoder
->encState
!= ES_Init
) {
905 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
906 if(ecOid
->Data
!= NULL
) {
909 cmsCopyCmsData(eContentType
, ecOid
);
910 return errSecSuccess
;
913 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
914 CMSEncoderRef cmsEncoder
,
915 CFTypeRef eContentTypeOID
)
917 // convert eContentTypeOID to a CSSM_OID
918 CSSM_OID contentType
= { 0, NULL
};
919 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
921 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
922 if (contentType
.Data
)
923 free(contentType
.Data
);
928 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
930 OSStatus
CMSEncoderCopyEncapsulatedContentType(
931 CMSEncoderRef cmsEncoder
,
932 CFDataRef
*eContentType
)
934 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
938 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
939 if(ecOid
->Data
== NULL
) {
940 *eContentType
= NULL
;
943 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
945 return errSecSuccess
;
949 * Optionally specify signed attributes. Only meaningful when creating a
950 * signed message. If this is called, it must be called before
951 * CMSEncoderUpdateContent().
953 OSStatus
CMSEncoderAddSignedAttributes(
954 CMSEncoderRef cmsEncoder
,
955 CMSSignedAttributes signedAttributes
)
957 if(cmsEncoder
== NULL
) {
960 if(cmsEncoder
->encState
!= ES_Init
) {
963 cmsEncoder
->signedAttributes
= signedAttributes
;
964 return errSecSuccess
;
968 * Set the signing time for a CMSEncoder.
969 * This is only used if the kCMSAttrSigningTime attribute is included.
971 OSStatus
CMSEncoderSetSigningTime(
972 CMSEncoderRef cmsEncoder
,
975 if(cmsEncoder
== NULL
) {
978 if(cmsEncoder
->encState
!= ES_Init
) {
981 cmsEncoder
->signingTime
= time
;
982 return errSecSuccess
;
986 OSStatus
CMSEncoderSetCertificateChainMode(
987 CMSEncoderRef cmsEncoder
,
988 CMSCertificateChainMode chainMode
)
990 if(cmsEncoder
== NULL
) {
993 if(cmsEncoder
->encState
!= ES_Init
) {
997 case kCMSCertificateNone
:
998 case kCMSCertificateSignerOnly
:
999 case kCMSCertificateChain
:
1000 case kCMSCertificateChainWithRoot
:
1005 cmsEncoder
->chainMode
= chainMode
;
1006 return errSecSuccess
;
1009 OSStatus
CMSEncoderGetCertificateChainMode(
1010 CMSEncoderRef cmsEncoder
,
1011 CMSCertificateChainMode
*chainModeOut
)
1013 if(cmsEncoder
== NULL
) {
1016 *chainModeOut
= cmsEncoder
->chainMode
;
1017 return errSecSuccess
;
1021 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1023 if (cmsEncoder
->cmsMsg
)
1024 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1028 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1030 if (cmsEncoder
->cmsMsg
)
1031 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1034 #pragma mark --- Action ---
1037 * Feed content bytes into the encoder.
1038 * Can be called multiple times.
1039 * No 'setter' routines can be called after this function has been called.
1041 OSStatus
CMSEncoderUpdateContent(
1042 CMSEncoderRef cmsEncoder
,
1043 const void *content
,
1046 if(cmsEncoder
== NULL
) {
1050 OSStatus ortn
= errSecSuccess
;
1051 switch(cmsEncoder
->encState
) {
1054 * First time thru: do the CmsMsg setup.
1056 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1060 /* fall thru to set up the encoder */
1063 /* We have a cmsMsg but no encoder; create one */
1064 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1065 ASSERT(cmsEncoder
->encoder
== NULL
);
1066 ortn
= cmsSetupEncoder(cmsEncoder
);
1070 /* only legal calls now are update and finalize */
1071 cmsEncoder
->encState
= ES_Updating
;
1075 ASSERT(cmsEncoder
->encoder
!= NULL
);
1079 /* Too late for another update */
1083 return errSecInternalComponent
;
1086 /* FIXME - CFIndex same size as size_t on 64bit? */
1087 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1089 ortn
= cmsRtnToOSStatus(ortn
);
1090 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1096 * Finish encoding the message and obtain the encoded result.
1097 * Caller must CFRelease the result.
1099 OSStatus
CMSEncoderCopyEncodedContent(
1100 CMSEncoderRef cmsEncoder
,
1101 CFDataRef
*encodedContent
)
1103 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1109 switch(cmsEncoder
->encState
) {
1111 /* normal termination */
1114 /* already been called */
1119 * The only time these are legal is when we're doing a SignedData
1120 * with certificates only (no signers, no content).
1122 if((cmsEncoder
->signers
!= NULL
) ||
1123 (cmsEncoder
->recipients
!= NULL
) ||
1124 (cmsEncoder
->otherCerts
== NULL
)) {
1128 /* Set up for certs only */
1129 ortn
= cmsSetupForSignedData(cmsEncoder
);
1133 /* and an encoder */
1134 ortn
= cmsSetupEncoder(cmsEncoder
);
1142 ASSERT(cmsEncoder
->encoder
!= NULL
);
1143 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1144 /* regardless of the outcome, the encoder itself has been freed */
1145 cmsEncoder
->encoder
= NULL
;
1147 return cmsRtnToOSStatus(ortn
);
1149 cmsEncoder
->encState
= ES_Final
;
1151 if((cmsEncoder
->encoderOut
.Data
== NULL
) && !cmsEncoder
->customCoder
) {
1152 /* not sure how this could happen... */
1153 dprintf("Successful encode, but no data\n");
1154 return errSecInternalComponent
;
1156 if(cmsEncoder
->customCoder
) {
1158 *encodedContent
= NULL
;
1159 return errSecSuccess
;
1162 /* in two out of three cases, we're done */
1163 switch(cmsEncoder
->op
) {
1166 *encodedContent
= CFDataCreate(NULL
, (const UInt8
*)cmsEncoder
->encoderOut
.Data
,
1167 cmsEncoder
->encoderOut
.Length
);
1168 return errSecSuccess
;
1169 case EO_SignEncrypt
:
1170 /* proceed, more work to do */
1175 * Signing & encrypting.
1176 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1177 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1178 * inside of the ContentInfo we just created as the data to encrypt.
1180 SecAsn1CoderRef asn1Coder
= NULL
;
1181 CSSM_DATA signedData
= {0, NULL
};
1183 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1187 ortn
= cmsContentInfoContent(asn1Coder
, &cmsEncoder
->encoderOut
, &signedData
);
1192 /* now just encrypt that, one-shot */
1193 ortn
= CMSEncode(NULL
, /* no signers this time */
1194 cmsEncoder
->recipients
,
1195 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1196 * encode the signedData - this asserts the
1197 * SEC_OID_OTHER OID tag in the EnvelopedData's
1199 FALSE
, /* detachedContent */
1200 kCMSAttrNone
, /* signedAttributes - none this time */
1201 signedData
.Data
, signedData
.Length
,
1206 SecAsn1CoderRelease(asn1Coder
);
1211 #pragma mark --- High-level API ---
1214 * High-level, one-shot encoder function.
1218 CFTypeRef recipients
,
1219 const CSSM_OID
*eContentType
,
1220 Boolean detachedContent
,
1221 CMSSignedAttributes signedAttributes
,
1222 const void *content
,
1224 CFDataRef
*encodedContent
) /* RETURNED */
1226 if((signers
== NULL
) && (recipients
== NULL
)) {
1229 if(encodedContent
== NULL
) {
1233 CMSEncoderRef cmsEncoder
;
1236 /* set up the encoder */
1237 ortn
= CMSEncoderCreate(&cmsEncoder
);
1242 /* subsequent errors to errOut: */
1244 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1250 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1256 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1261 if(detachedContent
) {
1262 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1267 if(signedAttributes
) {
1268 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1274 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1278 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1281 CFRelease(cmsEncoder
);
1285 OSStatus
CMSEncodeContent(
1287 CFTypeRef recipients
,
1288 CFTypeRef eContentTypeOID
,
1289 Boolean detachedContent
,
1290 CMSSignedAttributes signedAttributes
,
1291 const void *content
,
1293 CFDataRef
*encodedContentOut
) /* RETURNED */
1295 // convert eContentTypeOID to a CSSM_OID
1296 CSSM_OID contentType
= { 0, NULL
};
1297 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1299 const CSSM_OID
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1300 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1301 detachedContent
, signedAttributes
,
1302 content
, contentLen
, encodedContentOut
);
1303 if (contentType
.Data
)
1304 free(contentType
.Data
);
1308 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1311 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1312 * If we don't have a SecCmsMessageRef yet, we create one now.
1313 * This is the only place where we go to state ES_Msg.
1315 OSStatus
CMSEncoderGetCmsMessage(
1316 CMSEncoderRef cmsEncoder
,
1317 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1319 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1322 if(cmsEncoder
->cmsMsg
!= NULL
) {
1323 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1324 *cmsMessage
= cmsEncoder
->cmsMsg
;
1325 return errSecSuccess
;
1328 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1332 *cmsMessage
= cmsEncoder
->cmsMsg
;
1334 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1335 cmsEncoder
->encState
= ES_Msg
;
1336 return errSecSuccess
;
1340 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1341 * If this is called, it must be called before the first call to
1342 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1343 * incoming SecCmsEncoderRef.
1345 OSStatus
CMSEncoderSetEncoder(
1346 CMSEncoderRef cmsEncoder
,
1347 SecCmsEncoderRef encoder
)
1349 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1355 switch(cmsEncoder
->encState
) {
1357 /* No message, no encoder */
1358 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1359 ASSERT(cmsEncoder
->encoder
== NULL
);
1360 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1364 /* drop thru to set encoder */
1366 /* cmsMsg but no encoder */
1367 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1368 ASSERT(cmsEncoder
->encoder
== NULL
);
1369 cmsEncoder
->encoder
= encoder
;
1370 cmsEncoder
->encState
= ES_Updating
;
1371 cmsEncoder
->customCoder
= true; /* we won't see data */
1372 return errSecSuccess
;
1374 /* no can do, too late */
1380 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1381 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1382 * CMSEncoderUpdateContent() has been called.
1383 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1385 OSStatus
CMSEncoderGetEncoder(
1386 CMSEncoderRef cmsEncoder
,
1387 SecCmsEncoderRef
*encoder
) /* RETURNED */
1389 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1393 /* any state, whether we have an encoder or not is OK */
1394 *encoder
= cmsEncoder
->encoder
;
1395 return errSecSuccess
;
1398 #include <AssertMacros.h>
1401 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1402 * present. This timestamp is an authenticated timestamp provided by
1403 * a timestamping authority.
1405 * Returns errSecParam if the CMS message was not signed or if signerIndex
1406 * is greater than the number of signers of the message minus one.
1408 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1410 OSStatus
CMSEncoderCopySignerTimestamp(
1411 CMSEncoderRef cmsEncoder
,
1412 size_t signerIndex
, /* usually 0 */
1413 CFAbsoluteTime
*timestamp
) /* RETURNED */
1415 return CMSEncoderCopySignerTimestampWithPolicy(
1422 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1423 CMSEncoderRef cmsEncoder
,
1424 CFTypeRef timeStampPolicy
,
1425 size_t signerIndex
, /* usually 0 */
1426 CFAbsoluteTime
*timestamp
) /* RETURNED */
1428 OSStatus status
= errSecParam
;
1429 SecCmsMessageRef cmsg
;
1430 SecCmsSignedDataRef signedData
= NULL
;
1431 int numContentInfos
= 0;
1433 require(cmsEncoder
&& timestamp
, xit
);
1434 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1435 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1436 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1438 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1439 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1440 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1441 if ((signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
))))
1442 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
))
1444 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);