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 SECOidTag digestalgtag
;
98 CMSCertificateChainMode chainMode
;
101 static void cmsEncoderInit(CFTypeRef enc
);
102 static void cmsEncoderFinalize(CFTypeRef enc
);
104 static CFRuntimeClass cmsEncoderRuntimeClass
=
111 NULL
, /* equal - just use pointer equality */
112 NULL
, /* hash, ditto */
113 NULL
, /* copyFormattingDesc */
114 NULL
/* copyDebugDesc */
118 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
120 #pragma mark --- Private routines ---
123 * Decode a CFStringRef representation of an integer
125 static int cfStringToNumber(
130 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
136 * Encode an integer component of an OID, return resulting number of bytes;
137 * actual bytes are mallocd and returned in *encodeArray.
139 static unsigned encodeNumber(
141 unsigned char **encodeArray
) // mallocd and RETURNED
143 unsigned char *result
;
145 unsigned numDigits
= 0;
148 /* trival case - 0 maps to 0 */
150 *encodeArray
= (unsigned char *)malloc(1);
155 /* first calculate the number of digits in num, base 128 */
156 scratch
= (unsigned)num
;
157 while(scratch
!= 0) {
162 result
= (unsigned char *)malloc(numDigits
);
163 scratch
= (unsigned)num
;
164 for(dex
=0; dex
<numDigits
; dex
++) {
165 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
169 /* all digits except the last one have m.s. bit set */
170 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
174 *encodeArray
= result
;
179 * Given an OID in dotted-decimal string representation, convert to binary
180 * DER format. Returns a pointer in outOid which the caller must free(),
181 * as well as the length of the data in outLen.
182 * Function returns 0 if successful, non-zero otherwise.
184 static int encodeOid(
185 const unsigned char *inStr
,
186 unsigned char **outOid
,
187 unsigned int *outLen
)
189 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
190 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
192 unsigned numDigitBytes
; /* total #of output chars */
193 unsigned char firstByte
;
195 CFIndex numsToProcess
;
196 CFStringRef oidStr
= NULL
;
197 CFArrayRef argvRef
= NULL
;
201 /* parse input string into array of substrings */
202 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
203 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
204 if (!oidStr
) goto cleanExit
;
205 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
206 if (!argvRef
) goto cleanExit
;
207 argc
= CFArrayGetCount(argvRef
);
208 if (argc
< 3) goto cleanExit
;
210 /* first two numbers in OID munge together */
211 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
212 if (num
< 0) goto cleanExit
;
213 firstByte
= (40 * num
);
214 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
215 if (num
< 0) goto cleanExit
;
219 numsToProcess
= argc
- 2;
220 if(numsToProcess
> 0) {
221 /* skip this loop in the unlikely event that input is only two numbers */
222 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
223 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
224 for(digit
=0; digit
<numsToProcess
; digit
++) {
225 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
226 if (num
< 0) goto cleanExit
;
227 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
228 numDigitBytes
+= numDigits
[digit
];
231 *outLen
= (2 + numDigitBytes
);
232 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
234 *outP
++ = numDigitBytes
;
236 for(digit
=0; digit
<numsToProcess
; digit
++) {
237 unsigned int byteDex
;
238 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
239 *outP
++ = digits
[digit
][byteDex
];
243 for(digit
=0; digit
<numsToProcess
; digit
++) {
252 if (oidStr
) CFRelease(oidStr
);
253 if (argvRef
) CFRelease(argvRef
);
259 * Given a CF object reference describing an OID, convert to binary DER format
260 * and fill out the CSSM_OID structure provided by the caller. Caller is
261 * responsible for freeing the data pointer in outOid->Data.
263 * Function returns 0 if successful, non-zero otherwise.
266 static int convertOid(
270 if (!inRef
|| !outOid
)
273 unsigned char *oidData
= NULL
;
274 unsigned int oidLen
= 0;
276 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
277 // CFStringRef: OID representation is a dotted-decimal string
278 CFStringRef inStr
= (CFStringRef
)inRef
;
279 CFIndex max
= CFStringGetLength(inStr
) * 3;
281 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
284 if(encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0)
287 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
288 // CFDataRef: OID representation is in binary DER format
289 CFDataRef inData
= (CFDataRef
)inRef
;
290 oidLen
= (unsigned int) CFDataGetLength(inData
);
291 oidData
= (unsigned char *) malloc(oidLen
);
292 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
295 // Not in a format we understand
298 outOid
->Length
= oidLen
;
299 outOid
->Data
= (uint8
*)oidData
;
303 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
305 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
306 static void cmsEncoderClassInitialize(void)
309 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
312 /* init called out from _CFRuntimeCreateInstance() */
313 static void cmsEncoderInit(CFTypeRef enc
)
315 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
316 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
320 * Dispose of a CMSEncoder. Called out from CFRelease().
322 static void cmsEncoderFinalize(
325 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
326 if(cmsEncoder
== NULL
) {
329 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
330 free(cmsEncoder
->eContentType
.Data
);
332 CFRELEASE(cmsEncoder
->signers
);
333 CFRELEASE(cmsEncoder
->recipients
);
334 CFRELEASE(cmsEncoder
->otherCerts
);
335 if(cmsEncoder
->cmsMsg
!= NULL
) {
336 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
338 if(cmsEncoder
->arena
!= NULL
) {
339 SecArenaPoolFree(cmsEncoder
->arena
, false);
341 if(cmsEncoder
->encoder
!= NULL
) {
343 * Normally this gets freed in SecCmsEncoderFinish - this is
346 SecCmsEncoderDestroy(cmsEncoder
->encoder
);
350 static OSStatus
cmsSetupEncoder(
351 CMSEncoderRef cmsEncoder
)
355 ASSERT(cmsEncoder
->arena
== NULL
);
356 ASSERT(cmsEncoder
->encoder
== NULL
);
358 ortn
= SecArenaPoolCreate(1024, &cmsEncoder
->arena
);
360 return cmsRtnToOSStatus(ortn
);
362 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
363 NULL
, NULL
, // no callback
364 &cmsEncoder
->encoderOut
, // data goes here
366 NULL
, NULL
, // no password callback (right?)
367 NULL
, NULL
, // decrypt key callback
368 NULL
, NULL
, // detached digests
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(NULL
);
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(cmsEncoder
->cmsMsg
, contentInfo
,
405 return cmsRtnToOSStatus(ortn
);
407 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
408 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
409 /* Override the default eContentType of id-data */
410 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
412 NULL
, /* data - provided to encoder, not here */
413 cmsEncoder
->detachedContent
,
414 &cmsEncoder
->eContentType
);
417 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
419 NULL
, /* data - provided to encoder, not here */
420 cmsEncoder
->detachedContent
);
423 ortn
= cmsRtnToOSStatus(ortn
);
424 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
428 /* optional 'global' (per-SignedData) certs */
429 if(cmsEncoder
->otherCerts
!= NULL
) {
430 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
432 ortn
= cmsRtnToOSStatus(ortn
);
433 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
438 /* SignerInfos, one per signer */
439 CFIndex numSigners
= 0;
440 if(cmsEncoder
->signers
!= NULL
) {
441 /* this is optional...in case we're just creating a cert bundle */
442 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
445 SecKeychainRef ourKc
= NULL
;
446 SecCertificateRef ourCert
= NULL
;
447 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
449 switch(cmsEncoder
->chainMode
) {
450 case kCMSCertificateNone
:
451 chainMode
= SecCmsCMNone
;
453 case kCMSCertificateSignerOnly
:
454 chainMode
= SecCmsCMCertOnly
;
456 case kCMSCertificateChainWithRoot
:
457 chainMode
= SecCmsCMCertChainWithRoot
;
462 for(dex
=0; dex
<numSigners
; dex
++) {
463 SecCmsSignerInfoRef signerInfo
;
465 SecIdentityRef ourId
=
466 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
467 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
469 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
472 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)ourCert
, &ourKc
);
474 CSSM_PERROR("SecKeychainItemCopyKeychain", ortn
);
477 signerInfo
= SecCmsSignerInfoCreate(cmsEncoder
->cmsMsg
, ourId
, cmsEncoder
->digestalgtag
);
478 if (signerInfo
== NULL
) {
479 ortn
= errSecInternalComponent
;
483 /* we want the cert chain included for this one */
484 /* NOTE the usage parameter is currently unused by the SMIME lib */
485 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
486 certUsageEmailSigner
);
488 ortn
= cmsRtnToOSStatus(ortn
);
489 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
494 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
495 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
497 ortn
= cmsRtnToOSStatus(ortn
);
498 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
502 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
503 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, ourKc
);
505 ortn
= cmsRtnToOSStatus(ortn
);
506 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
510 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
511 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, ourKc
);
513 ortn
= cmsRtnToOSStatus(ortn
);
514 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
518 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
519 if (cmsEncoder
->signingTime
== 0)
520 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
521 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
523 ortn
= cmsRtnToOSStatus(ortn
);
524 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
529 ortn
= SecCmsSignedDataAddSignerInfo(signedData
, signerInfo
);
531 ortn
= cmsRtnToOSStatus(ortn
);
532 CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn
);
549 * Set up a SecCmsMessageRef for a EnvelopedData creation.
551 static OSStatus
cmsSetupForEnvelopedData(
552 CMSEncoderRef cmsEncoder
)
554 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
555 ASSERT(cmsEncoder
->recipients
!= NULL
);
557 SecCmsContentInfoRef contentInfo
= NULL
;
558 SecCmsEnvelopedDataRef envelopedData
= NULL
;
559 SECOidTag algorithmTag
;
564 * Find encryption algorithm...unfortunately we need a NULL-terminated array
565 * of SecCertificateRefs for this.
567 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
569 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
570 (numCerts
+1) * sizeof(SecCertificateRef
));
572 for(dex
=0; dex
<numCerts
; dex
++) {
573 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
574 cmsEncoder
->recipients
, dex
);
576 certArray
[numCerts
] = NULL
;
577 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
580 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
584 /* build chain of objects: message->envelopedData->data */
585 if(cmsEncoder
->cmsMsg
!= NULL
) {
586 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
588 cmsEncoder
->cmsMsg
= SecCmsMessageCreate(NULL
);
589 if(cmsEncoder
->cmsMsg
== NULL
) {
590 return errSecInternalComponent
;
592 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
593 algorithmTag
, keySize
);
594 if(envelopedData
== NULL
) {
595 return errSecInternalComponent
;
597 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
598 ortn
= SecCmsContentInfoSetContentEnvelopedData(cmsEncoder
->cmsMsg
,
599 contentInfo
, envelopedData
);
601 ortn
= cmsRtnToOSStatus(ortn
);
602 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
605 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
606 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
607 /* Override the default ContentType of id-data */
608 ortn
= SecCmsContentInfoSetContentOther(cmsEncoder
->cmsMsg
,
610 NULL
, /* data - provided to encoder, not here */
611 FALSE
, /* detachedContent */
612 &cmsEncoder
->eContentType
);
615 ortn
= SecCmsContentInfoSetContentData(cmsEncoder
->cmsMsg
,
617 NULL
/* data - provided to encoder, not here */,
618 cmsEncoder
->detachedContent
);
621 ortn
= cmsRtnToOSStatus(ortn
);
622 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
627 * create & attach recipient information, one for each recipient
629 for(dex
=0; dex
<numCerts
; dex
++) {
630 SecCmsRecipientInfoRef recipientInfo
= NULL
;
632 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
633 cmsEncoder
->recipients
, dex
);
634 recipientInfo
= SecCmsRecipientInfoCreate(cmsEncoder
->cmsMsg
, thisRecip
);
635 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
637 ortn
= cmsRtnToOSStatus(ortn
);
638 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
642 return errSecSuccess
;
646 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
647 * from CMSEncodeGetCmsMessage().
649 static OSStatus
cmsSetupCmsMsg(
650 CMSEncoderRef cmsEncoder
)
652 ASSERT(cmsEncoder
!= NULL
);
653 ASSERT(cmsEncoder
->encState
== ES_Init
);
655 /* figure out what high-level operation we're doing */
656 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
657 if(cmsEncoder
->recipients
!= NULL
) {
658 cmsEncoder
->op
= EO_SignEncrypt
;
661 cmsEncoder
->op
= EO_Sign
;
664 else if(cmsEncoder
->recipients
!= NULL
) {
665 cmsEncoder
->op
= EO_Encrypt
;
668 dprintf("CMSEncoderUpdateContent: nothing to do\n");
672 OSStatus ortn
= errSecSuccess
;
674 switch(cmsEncoder
->op
) {
677 /* If we're signing & encrypting, do the signing first */
678 ortn
= cmsSetupForSignedData(cmsEncoder
);
681 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
684 cmsEncoder
->encState
= ES_Msg
;
689 * ASN.1 template for decoding a ContentInfo.
692 CSSM_OID contentType
;
696 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
697 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
698 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
699 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
700 offsetof(SimpleContentInfo
, content
),
701 kSecAsn1AnyTemplate
},
706 * Obtain the content of a contentInfo, This basically strips off the contentType OID
707 * and returns its ASN_ANY content, allocated the provided coder's memory space.
709 static OSStatus
cmsContentInfoContent(
710 SecAsn1CoderRef asn1Coder
,
711 const CSSM_DATA
*contentInfo
,
712 CSSM_DATA
*content
) /* RETURNED */
715 SimpleContentInfo decodedInfo
;
717 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
718 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
719 cmsSimpleContentInfoTemplate
, &decodedInfo
);
723 if(decodedInfo
.content
.Data
== NULL
) {
724 dprintf("***Error decoding contentInfo: no content\n");
725 return errSecInternalComponent
;
727 *content
= decodedInfo
.content
;
728 return errSecSuccess
;
731 #pragma mark --- Start of Public API ---
733 CFTypeID
CMSEncoderGetTypeID(void)
735 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
737 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
738 pthread_once(&once
, &cmsEncoderClassInitialize
);
740 return cmsEncoderTypeID
;
744 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
746 OSStatus
CMSEncoderCreate(
747 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
749 CMSEncoderRef cmsEncoder
= NULL
;
751 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
752 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
754 if(cmsEncoder
== NULL
) {
755 return errSecAllocate
;
757 cmsEncoder
->encState
= ES_Init
;
758 cmsEncoder
->chainMode
= kCMSCertificateChain
;
759 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
760 *cmsEncoderOut
= cmsEncoder
;
761 return errSecSuccess
;
764 #pragma mark --- Getters & Setters ---
766 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1
= CFSTR("sha1");
767 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256
= CFSTR("sha256");
770 OSStatus
CMSEncoderSetSignerAlgorithm(
771 CMSEncoderRef cmsEncoder
,
772 CFStringRef digestAlgorithm
)
774 if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA1
)) {
775 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
776 } else if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA256
)) {
777 cmsEncoder
->digestalgtag
= SEC_OID_SHA256
;
782 return errSecSuccess
;
786 * Specify signers of the CMS message; implies that the message will be signed.
788 OSStatus
CMSEncoderAddSigners(
789 CMSEncoderRef cmsEncoder
,
790 CFTypeRef signerOrArray
)
792 if(cmsEncoder
== NULL
) {
795 if(cmsEncoder
->encState
!= ES_Init
) {
798 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
802 * Obtain an array of signers as specified in CMSEncoderSetSigners().
804 OSStatus
CMSEncoderCopySigners(
805 CMSEncoderRef cmsEncoder
,
808 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
811 if(cmsEncoder
->signers
!= NULL
) {
812 CFRetain(cmsEncoder
->signers
);
814 *signers
= cmsEncoder
->signers
;
815 return errSecSuccess
;
819 * Specify recipients of the message. Implies that the message will be encrypted.
821 OSStatus
CMSEncoderAddRecipients(
822 CMSEncoderRef cmsEncoder
,
823 CFTypeRef recipientOrArray
)
825 if(cmsEncoder
== NULL
) {
828 if(cmsEncoder
->encState
!= ES_Init
) {
831 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
832 SecCertificateGetTypeID());
836 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
838 OSStatus
CMSEncoderCopyRecipients(
839 CMSEncoderRef cmsEncoder
,
840 CFArrayRef
*recipients
)
842 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
845 if(cmsEncoder
->recipients
!= NULL
) {
846 CFRetain(cmsEncoder
->recipients
);
848 *recipients
= cmsEncoder
->recipients
;
849 return errSecSuccess
;
853 * Specify additional certs to include in a signed message.
855 OSStatus
CMSEncoderAddSupportingCerts(
856 CMSEncoderRef cmsEncoder
,
857 CFTypeRef certOrArray
)
859 if(cmsEncoder
== NULL
) {
862 if(cmsEncoder
->encState
!= ES_Init
) {
865 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
866 SecCertificateGetTypeID());
870 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
872 OSStatus
CMSEncoderCopySupportingCerts(
873 CMSEncoderRef cmsEncoder
,
874 CFArrayRef
*certs
) /* RETURNED */
876 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
879 if(cmsEncoder
->otherCerts
!= NULL
) {
880 CFRetain(cmsEncoder
->otherCerts
);
882 *certs
= cmsEncoder
->otherCerts
;
883 return errSecSuccess
;
886 OSStatus
CMSEncoderSetHasDetachedContent(
887 CMSEncoderRef cmsEncoder
,
888 Boolean detachedContent
)
890 if(cmsEncoder
== NULL
) {
893 if(cmsEncoder
->encState
!= ES_Init
) {
896 cmsEncoder
->detachedContent
= detachedContent
;
897 return errSecSuccess
;
900 OSStatus
CMSEncoderGetHasDetachedContent(
901 CMSEncoderRef cmsEncoder
,
902 Boolean
*detachedContent
) /* RETURNED */
904 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
907 *detachedContent
= cmsEncoder
->detachedContent
;
908 return errSecSuccess
;
912 * Optionally specify an eContentType OID for the inner EncapsulatedData for
913 * a signed message. The default eContentType, used of this function is not
914 * called, is id-data.
916 OSStatus
CMSEncoderSetEncapsulatedContentType(
917 CMSEncoderRef cmsEncoder
,
918 const CSSM_OID
*eContentType
)
920 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
923 if(cmsEncoder
->encState
!= ES_Init
) {
927 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
928 if(ecOid
->Data
!= NULL
) {
931 cmsCopyCmsData(eContentType
, ecOid
);
932 return errSecSuccess
;
935 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
936 CMSEncoderRef cmsEncoder
,
937 CFTypeRef eContentTypeOID
)
939 // convert eContentTypeOID to a CSSM_OID
940 CSSM_OID contentType
= { 0, NULL
};
941 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
943 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
944 if (contentType
.Data
)
945 free(contentType
.Data
);
950 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
952 OSStatus
CMSEncoderCopyEncapsulatedContentType(
953 CMSEncoderRef cmsEncoder
,
954 CFDataRef
*eContentType
)
956 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
960 CSSM_OID
*ecOid
= &cmsEncoder
->eContentType
;
961 if(ecOid
->Data
== NULL
) {
962 *eContentType
= NULL
;
965 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
967 return errSecSuccess
;
971 * Optionally specify signed attributes. Only meaningful when creating a
972 * signed message. If this is called, it must be called before
973 * CMSEncoderUpdateContent().
975 OSStatus
CMSEncoderAddSignedAttributes(
976 CMSEncoderRef cmsEncoder
,
977 CMSSignedAttributes signedAttributes
)
979 if(cmsEncoder
== NULL
) {
982 if(cmsEncoder
->encState
!= ES_Init
) {
985 cmsEncoder
->signedAttributes
= signedAttributes
;
986 return errSecSuccess
;
990 * Set the signing time for a CMSEncoder.
991 * This is only used if the kCMSAttrSigningTime attribute is included.
993 OSStatus
CMSEncoderSetSigningTime(
994 CMSEncoderRef cmsEncoder
,
997 if(cmsEncoder
== NULL
) {
1000 if(cmsEncoder
->encState
!= ES_Init
) {
1003 cmsEncoder
->signingTime
= time
;
1004 return errSecSuccess
;
1008 OSStatus
CMSEncoderSetCertificateChainMode(
1009 CMSEncoderRef cmsEncoder
,
1010 CMSCertificateChainMode chainMode
)
1012 if(cmsEncoder
== NULL
) {
1015 if(cmsEncoder
->encState
!= ES_Init
) {
1019 case kCMSCertificateNone
:
1020 case kCMSCertificateSignerOnly
:
1021 case kCMSCertificateChain
:
1022 case kCMSCertificateChainWithRoot
:
1027 cmsEncoder
->chainMode
= chainMode
;
1028 return errSecSuccess
;
1031 OSStatus
CMSEncoderGetCertificateChainMode(
1032 CMSEncoderRef cmsEncoder
,
1033 CMSCertificateChainMode
*chainModeOut
)
1035 if(cmsEncoder
== NULL
) {
1038 *chainModeOut
= cmsEncoder
->chainMode
;
1039 return errSecSuccess
;
1043 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1045 if (cmsEncoder
->cmsMsg
)
1046 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1050 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1052 if (cmsEncoder
->cmsMsg
)
1053 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1056 #pragma mark --- Action ---
1059 * Feed content bytes into the encoder.
1060 * Can be called multiple times.
1061 * No 'setter' routines can be called after this function has been called.
1063 OSStatus
CMSEncoderUpdateContent(
1064 CMSEncoderRef cmsEncoder
,
1065 const void *content
,
1068 if(cmsEncoder
== NULL
) {
1072 OSStatus ortn
= errSecSuccess
;
1073 switch(cmsEncoder
->encState
) {
1076 * First time thru: do the CmsMsg setup.
1078 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1082 /* fall thru to set up the encoder */
1085 /* We have a cmsMsg but no encoder; create one */
1086 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1087 ASSERT(cmsEncoder
->encoder
== NULL
);
1088 ortn
= cmsSetupEncoder(cmsEncoder
);
1092 /* only legal calls now are update and finalize */
1093 cmsEncoder
->encState
= ES_Updating
;
1097 ASSERT(cmsEncoder
->encoder
!= NULL
);
1101 /* Too late for another update */
1105 return errSecInternalComponent
;
1108 /* FIXME - CFIndex same size as size_t on 64bit? */
1109 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1111 ortn
= cmsRtnToOSStatus(ortn
);
1112 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1118 * Finish encoding the message and obtain the encoded result.
1119 * Caller must CFRelease the result.
1121 OSStatus
CMSEncoderCopyEncodedContent(
1122 CMSEncoderRef cmsEncoder
,
1123 CFDataRef
*encodedContent
)
1125 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1131 switch(cmsEncoder
->encState
) {
1133 /* normal termination */
1136 /* already been called */
1141 * The only time these are legal is when we're doing a SignedData
1142 * with certificates only (no signers, no content).
1144 if((cmsEncoder
->signers
!= NULL
) ||
1145 (cmsEncoder
->recipients
!= NULL
) ||
1146 (cmsEncoder
->otherCerts
== NULL
)) {
1150 /* Set up for certs only */
1151 ortn
= cmsSetupForSignedData(cmsEncoder
);
1155 /* and an encoder */
1156 ortn
= cmsSetupEncoder(cmsEncoder
);
1164 ASSERT(cmsEncoder
->encoder
!= NULL
);
1165 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1166 /* regardless of the outcome, the encoder itself has been freed */
1167 cmsEncoder
->encoder
= NULL
;
1169 return cmsRtnToOSStatus(ortn
);
1171 cmsEncoder
->encState
= ES_Final
;
1173 if((cmsEncoder
->encoderOut
.Data
== NULL
) && !cmsEncoder
->customCoder
) {
1174 /* not sure how this could happen... */
1175 dprintf("Successful encode, but no data\n");
1176 return errSecInternalComponent
;
1178 if(cmsEncoder
->customCoder
) {
1180 *encodedContent
= NULL
;
1181 return errSecSuccess
;
1184 /* in two out of three cases, we're done */
1185 switch(cmsEncoder
->op
) {
1188 *encodedContent
= CFDataCreate(NULL
, (const UInt8
*)cmsEncoder
->encoderOut
.Data
,
1189 cmsEncoder
->encoderOut
.Length
);
1190 return errSecSuccess
;
1191 case EO_SignEncrypt
:
1192 /* proceed, more work to do */
1197 * Signing & encrypting.
1198 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1199 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1200 * inside of the ContentInfo we just created as the data to encrypt.
1202 SecAsn1CoderRef asn1Coder
= NULL
;
1203 CSSM_DATA signedData
= {0, NULL
};
1205 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1209 ortn
= cmsContentInfoContent(asn1Coder
, &cmsEncoder
->encoderOut
, &signedData
);
1214 /* now just encrypt that, one-shot */
1215 ortn
= CMSEncode(NULL
, /* no signers this time */
1216 cmsEncoder
->recipients
,
1217 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1218 * encode the signedData - this asserts the
1219 * SEC_OID_OTHER OID tag in the EnvelopedData's
1221 FALSE
, /* detachedContent */
1222 kCMSAttrNone
, /* signedAttributes - none this time */
1223 signedData
.Data
, signedData
.Length
,
1228 SecAsn1CoderRelease(asn1Coder
);
1233 #pragma mark --- High-level API ---
1236 * High-level, one-shot encoder function.
1240 CFTypeRef recipients
,
1241 const CSSM_OID
*eContentType
,
1242 Boolean detachedContent
,
1243 CMSSignedAttributes signedAttributes
,
1244 const void *content
,
1246 CFDataRef
*encodedContent
) /* RETURNED */
1248 if((signers
== NULL
) && (recipients
== NULL
)) {
1251 if(encodedContent
== NULL
) {
1255 CMSEncoderRef cmsEncoder
;
1258 /* set up the encoder */
1259 ortn
= CMSEncoderCreate(&cmsEncoder
);
1264 /* subsequent errors to errOut: */
1266 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1272 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1278 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1283 if(detachedContent
) {
1284 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1289 if(signedAttributes
) {
1290 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1296 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1300 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1303 CFRelease(cmsEncoder
);
1307 OSStatus
CMSEncodeContent(
1309 CFTypeRef recipients
,
1310 CFTypeRef eContentTypeOID
,
1311 Boolean detachedContent
,
1312 CMSSignedAttributes signedAttributes
,
1313 const void *content
,
1315 CFDataRef
*encodedContentOut
) /* RETURNED */
1317 // convert eContentTypeOID to a CSSM_OID
1318 CSSM_OID contentType
= { 0, NULL
};
1319 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1321 const CSSM_OID
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1322 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1323 detachedContent
, signedAttributes
,
1324 content
, contentLen
, encodedContentOut
);
1325 if (contentType
.Data
)
1326 free(contentType
.Data
);
1330 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1333 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1334 * If we don't have a SecCmsMessageRef yet, we create one now.
1335 * This is the only place where we go to state ES_Msg.
1337 OSStatus
CMSEncoderGetCmsMessage(
1338 CMSEncoderRef cmsEncoder
,
1339 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1341 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1344 if(cmsEncoder
->cmsMsg
!= NULL
) {
1345 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1346 *cmsMessage
= cmsEncoder
->cmsMsg
;
1347 return errSecSuccess
;
1350 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1354 *cmsMessage
= cmsEncoder
->cmsMsg
;
1356 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1357 cmsEncoder
->encState
= ES_Msg
;
1358 return errSecSuccess
;
1362 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1363 * If this is called, it must be called before the first call to
1364 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1365 * incoming SecCmsEncoderRef.
1367 OSStatus
CMSEncoderSetEncoder(
1368 CMSEncoderRef cmsEncoder
,
1369 SecCmsEncoderRef encoder
)
1371 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1377 switch(cmsEncoder
->encState
) {
1379 /* No message, no encoder */
1380 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1381 ASSERT(cmsEncoder
->encoder
== NULL
);
1382 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1386 /* drop thru to set encoder */
1388 /* cmsMsg but no encoder */
1389 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1390 ASSERT(cmsEncoder
->encoder
== NULL
);
1391 cmsEncoder
->encoder
= encoder
;
1392 cmsEncoder
->encState
= ES_Updating
;
1393 cmsEncoder
->customCoder
= true; /* we won't see data */
1394 return errSecSuccess
;
1396 /* no can do, too late */
1402 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1403 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1404 * CMSEncoderUpdateContent() has been called.
1405 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1407 OSStatus
CMSEncoderGetEncoder(
1408 CMSEncoderRef cmsEncoder
,
1409 SecCmsEncoderRef
*encoder
) /* RETURNED */
1411 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1415 /* any state, whether we have an encoder or not is OK */
1416 *encoder
= cmsEncoder
->encoder
;
1417 return errSecSuccess
;
1420 #include <AssertMacros.h>
1423 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1424 * present. This timestamp is an authenticated timestamp provided by
1425 * a timestamping authority.
1427 * Returns errSecParam if the CMS message was not signed or if signerIndex
1428 * is greater than the number of signers of the message minus one.
1430 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1432 OSStatus
CMSEncoderCopySignerTimestamp(
1433 CMSEncoderRef cmsEncoder
,
1434 size_t signerIndex
, /* usually 0 */
1435 CFAbsoluteTime
*timestamp
) /* RETURNED */
1437 return CMSEncoderCopySignerTimestampWithPolicy(
1444 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1445 CMSEncoderRef cmsEncoder
,
1446 CFTypeRef timeStampPolicy
,
1447 size_t signerIndex
, /* usually 0 */
1448 CFAbsoluteTime
*timestamp
) /* RETURNED */
1450 OSStatus status
= errSecParam
;
1451 SecCmsMessageRef cmsg
;
1452 SecCmsSignedDataRef signedData
= NULL
;
1453 int numContentInfos
= 0;
1455 require(cmsEncoder
&& timestamp
, xit
);
1456 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1457 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1458 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1460 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1461 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1462 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1463 if ((signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
))))
1464 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
))
1466 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);