2 * Copyright (c) 2006-2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * CMSEncoder.c - encode, sign, and/or encrypt CMS messages.
28 #include "CMSEncoder.h"
30 #include <Security/SecBase.h>
31 #include <Security/SecCmsEncoder.h>
32 #include <Security/SecCmsEnvelopedData.h>
33 #include <Security/SecCmsMessage.h>
34 #include <Security/SecCmsRecipientInfo.h>
35 #include <Security/SecCmsSignedData.h>
36 #include <Security/SecCmsSignerInfo.h>
37 #include <Security/SecCmsContentInfo.h>
38 #include <Security/SecCertificate.h>
39 #include <Security/SecIdentity.h>
40 #include <Security/SecSMIME.h>
41 #include <Security/oidsattr.h>
42 #include <Security/SecAsn1Coder.h>
43 #include <Security/SecAsn1Types.h>
44 #include <Security/SecAsn1Templates.h>
45 #include <CoreFoundation/CFRuntime.h>
47 #include <utilities/SecCFRelease.h>
49 #include <security_smime/cmspriv.h>
51 #if TIMESTAMPING_SUPPORTED
52 #include <security_smime/tsaSupport.h>
55 #pragma mark --- Private types and definitions ---
61 ES_Init
, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent
62 * and CMSEncodeGetCmsMessage */
63 ES_Msg
, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */
64 ES_Updating
, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */
65 ES_Final
/* CMSEncoderCopyEncodedContent has been called */
69 * High-level operation: what are we doing?
78 * Caller's CMSEncoderRef points to one of these.
82 CMSEncoderState encState
;
84 Boolean detachedContent
;
85 SecAsn1Oid eContentType
;
86 CFMutableArrayRef signers
;
87 CFMutableArrayRef recipients
;
88 CFMutableArrayRef otherCerts
;
89 CMSSignedAttributes signedAttributes
;
90 CFAbsoluteTime signingTime
;
91 SecCmsMessageRef cmsMsg
; /* the encoder's arena */
92 SecCmsEncoderRef encoder
;
93 CFMutableDataRef encoderOut
; /* output goes here... */
94 bool customCoder
; /* unless this is set by
95 * CMSEncoderSetEncoder */
96 SECOidTag digestalgtag
;
98 CMSCertificateChainMode chainMode
;
99 CFDataRef hashAgilityAttrValue
;
102 static void cmsEncoderInit(CFTypeRef enc
);
103 static void cmsEncoderFinalize(CFTypeRef enc
);
105 static CFRuntimeClass cmsEncoderRuntimeClass
=
112 NULL
, /* equal - just use pointer equality */
113 NULL
, /* hash, ditto */
114 NULL
, /* copyFormattingDesc */
115 NULL
/* copyDebugDesc */
118 #if TIMESTAMPING_SUPPORTED
119 void CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
);
122 #pragma mark --- Private routines ---
125 * Decode a CFStringRef representation of an integer
127 static int cfStringToNumber(
132 if (!inStr
|| !CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
138 * Encode an integer component of an OID, return resulting number of bytes;
139 * actual bytes are mallocd and returned in *encodeArray.
141 static unsigned encodeNumber(
143 unsigned char **encodeArray
) // mallocd and RETURNED
145 unsigned char *result
;
147 unsigned numDigits
= 0;
150 /* trival case - 0 maps to 0 */
152 *encodeArray
= (unsigned char *)malloc(1);
157 /* first calculate the number of digits in num, base 128 */
158 scratch
= (unsigned)num
;
159 while(scratch
!= 0) {
164 result
= (unsigned char *)malloc(numDigits
);
165 scratch
= (unsigned)num
;
166 for(dex
=0; dex
<numDigits
; dex
++) {
167 result
[numDigits
- dex
- 1] = scratch
& 0x7f;
171 /* all digits except the last one have m.s. bit set */
172 for(dex
=0; dex
<(numDigits
- 1); dex
++) {
176 *encodeArray
= result
;
181 * Given an OID in dotted-decimal string representation, convert to binary
182 * DER format. Returns a pointer in outOid which the caller must free(),
183 * as well as the length of the data in outLen.
184 * Function returns 0 if successful, non-zero otherwise.
186 static int encodeOid(
187 const unsigned char *inStr
,
188 unsigned char **outOid
,
189 unsigned int *outLen
)
191 unsigned char **digits
= NULL
; /* array of char * from encodeNumber */
192 unsigned *numDigits
= NULL
; /* array of unsigned from encodeNumber */
194 unsigned numDigitBytes
; /* total #of output chars */
195 unsigned char firstByte
;
197 CFIndex numsToProcess
;
198 CFStringRef oidStr
= NULL
;
199 CFArrayRef argvRef
= NULL
;
203 /* parse input string into array of substrings */
204 if (!inStr
|| !outOid
|| !outLen
) goto cleanExit
;
205 oidStr
= CFStringCreateWithCString(NULL
, (const char *)inStr
, kCFStringEncodingASCII
);
206 if (!oidStr
) goto cleanExit
;
207 argvRef
= CFStringCreateArrayBySeparatingStrings(NULL
, oidStr
, CFSTR("."));
208 if (!argvRef
) goto cleanExit
;
209 argc
= CFArrayGetCount(argvRef
);
210 if (argc
< 3) goto cleanExit
;
212 /* first two numbers in OID munge together */
213 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 0));
214 if (num
< 0) goto cleanExit
;
215 firstByte
= (40 * num
);
216 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, 1));
217 if (num
< 0) goto cleanExit
;
221 numsToProcess
= argc
- 2;
222 if(numsToProcess
> 0) {
223 /* skip this loop in the unlikely event that input is only two numbers */
224 digits
= (unsigned char **) malloc(numsToProcess
* sizeof(unsigned char *));
225 numDigits
= (unsigned *) malloc(numsToProcess
* sizeof(unsigned));
226 for(digit
=0; digit
<numsToProcess
; digit
++) {
227 num
= cfStringToNumber((CFStringRef
)CFArrayGetValueAtIndex(argvRef
, digit
+2));
228 if (num
< 0) goto cleanExit
;
229 numDigits
[digit
] = encodeNumber(num
, &digits
[digit
]);
230 numDigitBytes
+= numDigits
[digit
];
233 *outLen
= (2 + numDigitBytes
);
234 *outOid
= outP
= (unsigned char *) malloc(*outLen
);
236 *outP
++ = numDigitBytes
;
238 for(digit
=0; digit
<numsToProcess
; digit
++) {
239 unsigned int byteDex
;
240 for(byteDex
=0; byteDex
<numDigits
[digit
]; byteDex
++) {
241 *outP
++ = digits
[digit
][byteDex
];
245 for(digit
=0; digit
<numsToProcess
; digit
++) {
252 if (digits
) free(digits
);
253 if (numDigits
) free(numDigits
);
254 if (oidStr
) CFRelease(oidStr
);
255 if (argvRef
) CFRelease(argvRef
);
261 * Given a CF object reference describing an OID, convert to binary DER format
262 * and fill out the CSSM_OID structure provided by the caller. Caller is
263 * responsible for freeing the data pointer in outOid->Data.
265 * Function returns 0 if successful, non-zero otherwise.
268 static int convertOid(
272 if (!inRef
|| !outOid
)
275 unsigned char *oidData
= NULL
;
276 unsigned int oidLen
= 0;
278 if (CFGetTypeID(inRef
) == CFStringGetTypeID()) {
279 // CFStringRef: OID representation is a dotted-decimal string
280 CFStringRef inStr
= (CFStringRef
)inRef
;
281 CFIndex max
= CFStringGetLength(inStr
) * 3;
283 if (!CFStringGetCString(inStr
, buf
, max
-1, kCFStringEncodingASCII
))
286 if(encodeOid((unsigned char *)buf
, &oidData
, &oidLen
) != 0)
289 else if (CFGetTypeID(inRef
) == CFDataGetTypeID()) {
290 // CFDataRef: OID representation is in binary DER format
291 CFDataRef inData
= (CFDataRef
)inRef
;
292 oidLen
= (unsigned int) CFDataGetLength(inData
);
293 oidData
= (unsigned char *) malloc(oidLen
);
294 memcpy(oidData
, CFDataGetBytePtr(inData
), oidLen
);
297 // Not in a format we understand
300 outOid
->Length
= oidLen
;
301 outOid
->Data
= (uint8_t *)oidData
;
305 static CFTypeID cmsEncoderTypeID
= _kCFRuntimeNotATypeID
;
307 /* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
308 static void cmsEncoderClassInitialize(void)
311 _CFRuntimeRegisterClass((const CFRuntimeClass
* const)&cmsEncoderRuntimeClass
);
314 /* init called out from _CFRuntimeCreateInstance() */
315 static void cmsEncoderInit(CFTypeRef enc
)
317 char *start
= ((char *)enc
) + sizeof(CFRuntimeBase
);
318 memset(start
, 0, sizeof(struct _CMSEncoder
) - sizeof(CFRuntimeBase
));
322 * Dispose of a CMSEncoder. Called out from CFRelease().
324 static void cmsEncoderFinalize(
327 CMSEncoderRef cmsEncoder
= (CMSEncoderRef
)enc
;
328 if(cmsEncoder
== NULL
) {
331 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
332 free(cmsEncoder
->eContentType
.Data
);
334 CFRELEASE(cmsEncoder
->signers
);
335 CFRELEASE(cmsEncoder
->recipients
);
336 CFRELEASE(cmsEncoder
->otherCerts
);
337 if(cmsEncoder
->cmsMsg
!= NULL
) {
338 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
339 cmsEncoder
->cmsMsg
= NULL
;
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 cmsEncoder
->encoderOut
= CFDataCreateMutable(NULL
, 0);
359 if (!cmsEncoder
->encoderOut
) {
360 return errSecAllocate
;
363 ortn
= SecCmsEncoderCreate(cmsEncoder
->cmsMsg
,
364 NULL
, NULL
, // no callback
365 cmsEncoder
->encoderOut
, // data goes here
366 NULL
, NULL
, // no password callback (right?)
367 NULL
, NULL
, // decrypt key callback
368 &cmsEncoder
->encoder
);
370 return cmsRtnToOSStatus(ortn
);
372 return errSecSuccess
;
376 * Set up a SecCmsMessageRef for a SignedData creation.
378 static OSStatus
cmsSetupForSignedData(
379 CMSEncoderRef cmsEncoder
)
381 ASSERT((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
));
383 SecCmsContentInfoRef contentInfo
= NULL
;
384 SecCmsSignedDataRef signedData
= NULL
;
387 /* build chain of objects: message->signedData->data */
388 if(cmsEncoder
->cmsMsg
!= NULL
) {
389 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
391 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
392 if(cmsEncoder
->cmsMsg
== NULL
) {
393 return errSecInternalComponent
;
396 signedData
= SecCmsSignedDataCreate(cmsEncoder
->cmsMsg
);
397 if(signedData
== NULL
) {
398 return errSecInternalComponent
;
400 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
401 ortn
= SecCmsContentInfoSetContentSignedData(contentInfo
,signedData
);
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(contentInfo
,
409 NULL
, /* data - provided to encoder, not here */
410 cmsEncoder
->detachedContent
,
411 &cmsEncoder
->eContentType
);
414 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
415 NULL
, /* data - provided to encoder, not here */
416 cmsEncoder
->detachedContent
);
419 ortn
= cmsRtnToOSStatus(ortn
);
420 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn
);
424 /* optional 'global' (per-SignedData) certs */
425 if(cmsEncoder
->otherCerts
!= NULL
) {
426 ortn
= SecCmsSignedDataAddCertList(signedData
, cmsEncoder
->otherCerts
);
428 ortn
= cmsRtnToOSStatus(ortn
);
429 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn
);
434 /* SignerInfos, one per signer */
435 CFIndex numSigners
= 0;
436 if(cmsEncoder
->signers
!= NULL
) {
437 /* this is optional...in case we're just creating a cert bundle */
438 numSigners
= CFArrayGetCount(cmsEncoder
->signers
);
441 SecCertificateRef ourCert
= NULL
;
442 SecCmsCertChainMode chainMode
= SecCmsCMCertChain
;
444 switch(cmsEncoder
->chainMode
) {
445 case kCMSCertificateNone
:
446 chainMode
= SecCmsCMNone
;
448 case kCMSCertificateSignerOnly
:
449 chainMode
= SecCmsCMCertOnly
;
451 case kCMSCertificateChainWithRoot
:
452 chainMode
= SecCmsCMCertChainWithRoot
;
457 for(dex
=0; dex
<numSigners
; dex
++) {
458 SecCmsSignerInfoRef signerInfo
;
460 SecIdentityRef ourId
=
461 (SecIdentityRef
)CFArrayGetValueAtIndex(cmsEncoder
->signers
, dex
);
462 ortn
= SecIdentityCopyCertificate(ourId
, &ourCert
);
464 CSSM_PERROR("SecIdentityCopyCertificate", ortn
);
467 /* this creates the signerInfo and adds it to the signedData object */
468 signerInfo
= SecCmsSignerInfoCreate(signedData
, ourId
, cmsEncoder
->digestalgtag
);
469 if (signerInfo
== NULL
) {
470 ortn
= errSecInternalComponent
;
474 /* we want the cert chain included for this one */
475 /* NOTE the usage parameter is currently unused by the SMIME lib */
476 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, chainMode
,
477 certUsageEmailSigner
);
479 ortn
= cmsRtnToOSStatus(ortn
);
480 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn
);
485 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeCapabilities
) {
486 ortn
= SecCmsSignerInfoAddSMIMECaps(signerInfo
);
488 ortn
= cmsRtnToOSStatus(ortn
);
489 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
493 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeEncryptionKeyPrefs
) {
494 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
496 ortn
= cmsRtnToOSStatus(ortn
);
497 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
501 if(cmsEncoder
->signedAttributes
& kCMSAttrSmimeMSEncryptionKeyPrefs
) {
502 ortn
= SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo
, ourCert
, NULL
);
504 ortn
= cmsRtnToOSStatus(ortn
);
505 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn
);
509 if(cmsEncoder
->signedAttributes
& kCMSAttrSigningTime
) {
510 if (cmsEncoder
->signingTime
== 0)
511 cmsEncoder
->signingTime
= CFAbsoluteTimeGetCurrent();
512 ortn
= SecCmsSignerInfoAddSigningTime(signerInfo
, cmsEncoder
->signingTime
);
514 ortn
= cmsRtnToOSStatus(ortn
);
515 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn
);
519 if(cmsEncoder
->signedAttributes
& kCMSAttrAppleCodesigningHashAgility
) {
520 ortn
= SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo
, cmsEncoder
->hashAgilityAttrValue
);
521 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
522 CFReleaseNull(cmsEncoder
->hashAgilityAttrValue
);
524 ortn
= cmsRtnToOSStatus(ortn
);
525 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn
);
540 * Set up a SecCmsMessageRef for a EnvelopedData creation.
542 static OSStatus
cmsSetupForEnvelopedData(
543 CMSEncoderRef cmsEncoder
)
545 ASSERT(cmsEncoder
->op
== EO_Encrypt
);
546 ASSERT(cmsEncoder
->recipients
!= NULL
);
548 SecCmsContentInfoRef contentInfo
= NULL
;
549 SecCmsEnvelopedDataRef envelopedData
= NULL
;
550 SECOidTag algorithmTag
;
555 * Find encryption algorithm...unfortunately we need a NULL-terminated array
556 * of SecCertificateRefs for this.
558 CFIndex numCerts
= CFArrayGetCount(cmsEncoder
->recipients
);
560 SecCertificateRef
*certArray
= (SecCertificateRef
*)malloc(
561 (numCerts
+1) * sizeof(SecCertificateRef
));
563 for(dex
=0; dex
<numCerts
; dex
++) {
564 certArray
[dex
] = (SecCertificateRef
)CFArrayGetValueAtIndex(
565 cmsEncoder
->recipients
, dex
);
567 certArray
[numCerts
] = NULL
;
568 ortn
= SecSMIMEFindBulkAlgForRecipients(certArray
, &algorithmTag
, &keySize
);
571 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn
);
575 /* build chain of objects: message->envelopedData->data */
576 if(cmsEncoder
->cmsMsg
!= NULL
) {
577 SecCmsMessageDestroy(cmsEncoder
->cmsMsg
);
579 cmsEncoder
->cmsMsg
= SecCmsMessageCreate();
580 if(cmsEncoder
->cmsMsg
== NULL
) {
581 return errSecInternalComponent
;
583 envelopedData
= SecCmsEnvelopedDataCreate(cmsEncoder
->cmsMsg
,
584 algorithmTag
, keySize
);
585 if(envelopedData
== NULL
) {
586 return errSecInternalComponent
;
588 contentInfo
= SecCmsMessageGetContentInfo(cmsEncoder
->cmsMsg
);
589 ortn
= SecCmsContentInfoSetContentEnvelopedData(contentInfo
, envelopedData
);
591 ortn
= cmsRtnToOSStatus(ortn
);
592 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn
);
595 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
596 if(cmsEncoder
->eContentType
.Data
!= NULL
) {
597 /* Override the default ContentType of id-data */
598 ortn
= SecCmsContentInfoSetContentOther(contentInfo
,
599 NULL
, /* data - provided to encoder, not here */
600 FALSE
, /* detachedContent */
601 &cmsEncoder
->eContentType
);
604 ortn
= SecCmsContentInfoSetContentData(contentInfo
,
605 NULL
/* data - provided to encoder, not here */,
606 cmsEncoder
->detachedContent
);
609 ortn
= cmsRtnToOSStatus(ortn
);
610 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn
);
615 * create & attach recipient information, one for each recipient
617 for(dex
=0; dex
<numCerts
; dex
++) {
618 SecCmsRecipientInfoRef recipientInfo
= NULL
;
620 SecCertificateRef thisRecip
= (SecCertificateRef
)CFArrayGetValueAtIndex(
621 cmsEncoder
->recipients
, dex
);
622 recipientInfo
= SecCmsRecipientInfoCreate(envelopedData
, thisRecip
);
623 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
625 ortn
= cmsRtnToOSStatus(ortn
);
626 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn
);
630 return errSecSuccess
;
634 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
635 * from CMSEncodeGetCmsMessage().
637 static OSStatus
cmsSetupCmsMsg(
638 CMSEncoderRef cmsEncoder
)
640 ASSERT(cmsEncoder
!= NULL
);
641 ASSERT(cmsEncoder
->encState
== ES_Init
);
643 /* figure out what high-level operation we're doing */
644 if((cmsEncoder
->signers
!= NULL
) || (cmsEncoder
->otherCerts
!= NULL
)) {
645 if(cmsEncoder
->recipients
!= NULL
) {
646 cmsEncoder
->op
= EO_SignEncrypt
;
649 cmsEncoder
->op
= EO_Sign
;
652 else if(cmsEncoder
->recipients
!= NULL
) {
653 cmsEncoder
->op
= EO_Encrypt
;
656 dprintf("CMSEncoderUpdateContent: nothing to do\n");
660 OSStatus ortn
= errSecSuccess
;
662 switch(cmsEncoder
->op
) {
665 /* If we're signing & encrypting, do the signing first */
666 ortn
= cmsSetupForSignedData(cmsEncoder
);
669 ortn
= cmsSetupForEnvelopedData(cmsEncoder
);
672 cmsEncoder
->encState
= ES_Msg
;
677 * ASN.1 template for decoding a ContentInfo.
680 SecAsn1Oid contentType
;
684 static const SecAsn1Template cmsSimpleContentInfoTemplate
[] = {
685 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
686 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
687 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
688 offsetof(SimpleContentInfo
, content
),
689 kSecAsn1AnyTemplate
},
694 * Obtain the content of a contentInfo, This basically strips off the contentType OID
695 * and returns its ASN_ANY content, allocated the provided coder's memory space.
697 static OSStatus
cmsContentInfoContent(
698 SecAsn1CoderRef asn1Coder
,
699 const SecAsn1Item
*contentInfo
,
700 SecAsn1Item
*content
) /* RETURNED */
703 SimpleContentInfo decodedInfo
;
705 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
706 ortn
= SecAsn1DecodeData(asn1Coder
, contentInfo
,
707 cmsSimpleContentInfoTemplate
, &decodedInfo
);
711 if(decodedInfo
.content
.Data
== NULL
) {
712 dprintf("***Error decoding contentInfo: no content\n");
713 return errSecInternalComponent
;
715 *content
= decodedInfo
.content
;
716 return errSecSuccess
;
719 #pragma mark --- Start of Public API ---
721 CFTypeID
CMSEncoderGetTypeID(void)
723 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
725 if(cmsEncoderTypeID
== _kCFRuntimeNotATypeID
) {
726 pthread_once(&once
, &cmsEncoderClassInitialize
);
728 return cmsEncoderTypeID
;
732 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
734 OSStatus
CMSEncoderCreate(
735 CMSEncoderRef
*cmsEncoderOut
) /* RETURNED */
737 CMSEncoderRef cmsEncoder
= NULL
;
739 uint32_t extra
= sizeof(*cmsEncoder
) - sizeof(cmsEncoder
->base
);
740 cmsEncoder
= (CMSEncoderRef
)_CFRuntimeCreateInstance(NULL
, CMSEncoderGetTypeID(),
742 if(cmsEncoder
== NULL
) {
743 return errSecAllocate
;
745 cmsEncoder
->encState
= ES_Init
;
746 cmsEncoder
->chainMode
= kCMSCertificateChain
;
747 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
748 *cmsEncoderOut
= cmsEncoder
;
749 return errSecSuccess
;
752 #pragma mark --- Getters & Setters ---
754 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1
= CFSTR("sha1");
755 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256
= CFSTR("sha256");
758 OSStatus
CMSEncoderSetSignerAlgorithm(
759 CMSEncoderRef cmsEncoder
,
760 CFStringRef digestAlgorithm
)
762 if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA1
)) {
763 cmsEncoder
->digestalgtag
= SEC_OID_SHA1
;
764 } else if (CFEqual(digestAlgorithm
, kCMSEncoderDigestAlgorithmSHA256
)) {
765 cmsEncoder
->digestalgtag
= SEC_OID_SHA256
;
770 return errSecSuccess
;
774 * Specify signers of the CMS message; implies that the message will be signed.
776 OSStatus
CMSEncoderAddSigners(
777 CMSEncoderRef cmsEncoder
,
778 CFTypeRef signerOrArray
)
780 if(cmsEncoder
== NULL
) {
783 if(cmsEncoder
->encState
!= ES_Init
) {
786 return cmsAppendToArray(signerOrArray
, &cmsEncoder
->signers
, SecIdentityGetTypeID());
790 * Obtain an array of signers as specified in CMSEncoderSetSigners().
792 OSStatus
CMSEncoderCopySigners(
793 CMSEncoderRef cmsEncoder
,
796 if((cmsEncoder
== NULL
) || (signers
== NULL
)) {
799 if(cmsEncoder
->signers
!= NULL
) {
800 CFRetain(cmsEncoder
->signers
);
802 *signers
= cmsEncoder
->signers
;
803 return errSecSuccess
;
807 * Specify recipients of the message. Implies that the message will be encrypted.
809 OSStatus
CMSEncoderAddRecipients(
810 CMSEncoderRef cmsEncoder
,
811 CFTypeRef recipientOrArray
)
813 if(cmsEncoder
== NULL
) {
816 if(cmsEncoder
->encState
!= ES_Init
) {
819 return cmsAppendToArray(recipientOrArray
, &cmsEncoder
->recipients
,
820 SecCertificateGetTypeID());
824 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
826 OSStatus
CMSEncoderCopyRecipients(
827 CMSEncoderRef cmsEncoder
,
828 CFArrayRef
*recipients
)
830 if((cmsEncoder
== NULL
) || (recipients
== NULL
)) {
833 if(cmsEncoder
->recipients
!= NULL
) {
834 CFRetain(cmsEncoder
->recipients
);
836 *recipients
= cmsEncoder
->recipients
;
837 return errSecSuccess
;
841 * Specify additional certs to include in a signed message.
843 OSStatus
CMSEncoderAddSupportingCerts(
844 CMSEncoderRef cmsEncoder
,
845 CFTypeRef certOrArray
)
847 if(cmsEncoder
== NULL
) {
850 if(cmsEncoder
->encState
!= ES_Init
) {
853 return cmsAppendToArray(certOrArray
, &cmsEncoder
->otherCerts
,
854 SecCertificateGetTypeID());
858 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
860 OSStatus
CMSEncoderCopySupportingCerts(
861 CMSEncoderRef cmsEncoder
,
862 CFArrayRef
*certs
) /* RETURNED */
864 if((cmsEncoder
== NULL
) || (certs
== NULL
)) {
867 if(cmsEncoder
->otherCerts
!= NULL
) {
868 CFRetain(cmsEncoder
->otherCerts
);
870 *certs
= cmsEncoder
->otherCerts
;
871 return errSecSuccess
;
874 OSStatus
CMSEncoderSetHasDetachedContent(
875 CMSEncoderRef cmsEncoder
,
876 Boolean detachedContent
)
878 if(cmsEncoder
== NULL
) {
881 if(cmsEncoder
->encState
!= ES_Init
) {
884 cmsEncoder
->detachedContent
= detachedContent
;
885 return errSecSuccess
;
888 OSStatus
CMSEncoderGetHasDetachedContent(
889 CMSEncoderRef cmsEncoder
,
890 Boolean
*detachedContent
) /* RETURNED */
892 if((cmsEncoder
== NULL
) || (detachedContent
== NULL
)) {
895 *detachedContent
= cmsEncoder
->detachedContent
;
896 return errSecSuccess
;
900 * Optionally specify an eContentType OID for the inner EncapsulatedData for
901 * a signed message. The default eContentType, used of this function is not
902 * called, is id-data.
904 static OSStatus
CMSEncoderSetEncapsulatedContentType(
905 CMSEncoderRef cmsEncoder
,
906 const SecAsn1Oid
*eContentType
)
908 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
911 if(cmsEncoder
->encState
!= ES_Init
) {
915 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
916 if(ecOid
->Data
!= NULL
) {
919 cmsCopyCmsData(eContentType
, ecOid
);
920 return errSecSuccess
;
923 OSStatus
CMSEncoderSetEncapsulatedContentTypeOID(
924 CMSEncoderRef cmsEncoder
,
925 CFTypeRef eContentTypeOID
)
927 // convert eContentTypeOID to a CSSM_OID
928 SecAsn1Oid contentType
= { 0, NULL
};
929 if (!eContentTypeOID
|| convertOid(eContentTypeOID
, &contentType
) != 0)
931 OSStatus result
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, &contentType
);
932 if (contentType
.Data
)
933 free(contentType
.Data
);
938 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
940 OSStatus
CMSEncoderCopyEncapsulatedContentType(
941 CMSEncoderRef cmsEncoder
,
942 CFDataRef
*eContentType
)
944 if((cmsEncoder
== NULL
) || (eContentType
== NULL
)) {
948 SecAsn1Oid
*ecOid
= &cmsEncoder
->eContentType
;
949 if(ecOid
->Data
== NULL
) {
950 *eContentType
= NULL
;
953 *eContentType
= CFDataCreate(NULL
, ecOid
->Data
, ecOid
->Length
);
955 return errSecSuccess
;
959 * Optionally specify signed attributes. Only meaningful when creating a
960 * signed message. If this is called, it must be called before
961 * CMSEncoderUpdateContent().
963 OSStatus
CMSEncoderAddSignedAttributes(
964 CMSEncoderRef cmsEncoder
,
965 CMSSignedAttributes signedAttributes
)
967 if(cmsEncoder
== NULL
) {
970 if(cmsEncoder
->encState
!= ES_Init
) {
973 cmsEncoder
->signedAttributes
|= signedAttributes
;
974 return errSecSuccess
;
978 * Set the signing time for a CMSEncoder.
979 * This is only used if the kCMSAttrSigningTime attribute is included.
981 OSStatus
CMSEncoderSetSigningTime(
982 CMSEncoderRef cmsEncoder
,
985 if(cmsEncoder
== NULL
) {
988 if(cmsEncoder
->encState
!= ES_Init
) {
991 cmsEncoder
->signingTime
= time
;
992 return errSecSuccess
;
996 * Set the hash agility attribute for a CMSEncoder.
997 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute
1000 OSStatus
CMSEncoderSetAppleCodesigningHashAgility(
1001 CMSEncoderRef cmsEncoder
,
1002 CFDataRef hashAgilityAttrValue
)
1004 if (cmsEncoder
== NULL
|| cmsEncoder
->encState
!= ES_Init
) {
1007 cmsEncoder
->hashAgilityAttrValue
= CFRetainSafe(hashAgilityAttrValue
);
1008 return errSecSuccess
;
1011 OSStatus
CMSEncoderSetCertificateChainMode(
1012 CMSEncoderRef cmsEncoder
,
1013 CMSCertificateChainMode chainMode
)
1015 if(cmsEncoder
== NULL
) {
1018 if(cmsEncoder
->encState
!= ES_Init
) {
1022 case kCMSCertificateNone
:
1023 case kCMSCertificateSignerOnly
:
1024 case kCMSCertificateChain
:
1025 case kCMSCertificateChainWithRoot
:
1030 cmsEncoder
->chainMode
= chainMode
;
1031 return errSecSuccess
;
1034 OSStatus
CMSEncoderGetCertificateChainMode(
1035 CMSEncoderRef cmsEncoder
,
1036 CMSCertificateChainMode
*chainModeOut
)
1038 if(cmsEncoder
== NULL
) {
1041 *chainModeOut
= cmsEncoder
->chainMode
;
1042 return errSecSuccess
;
1045 #if TIMESTAMPING_SUPPORTED
1047 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder
, SecCmsTSACallback tsaCallback
)
1049 if (cmsEncoder
->cmsMsg
)
1050 SecCmsMessageSetTSACallback(cmsEncoder
->cmsMsg
, tsaCallback
);
1054 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder
, CFTypeRef tsaContext
)
1056 if (cmsEncoder
->cmsMsg
)
1057 SecCmsMessageSetTSAContext(cmsEncoder
->cmsMsg
, tsaContext
);
1061 #pragma mark --- Action ---
1064 * Feed content bytes into the encoder.
1065 * Can be called multiple times.
1066 * No 'setter' routines can be called after this function has been called.
1068 OSStatus
CMSEncoderUpdateContent(
1069 CMSEncoderRef cmsEncoder
,
1070 const void *content
,
1073 if(cmsEncoder
== NULL
) {
1077 OSStatus ortn
= errSecSuccess
;
1078 switch(cmsEncoder
->encState
) {
1081 * First time thru: do the CmsMsg setup.
1083 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1087 /* fall thru to set up the encoder */
1090 /* We have a cmsMsg but no encoder; create one */
1091 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1092 ASSERT(cmsEncoder
->encoder
== NULL
);
1093 ortn
= cmsSetupEncoder(cmsEncoder
);
1097 /* only legal calls now are update and finalize */
1098 cmsEncoder
->encState
= ES_Updating
;
1102 ASSERT(cmsEncoder
->encoder
!= NULL
);
1106 /* Too late for another update */
1110 return errSecInternalComponent
;
1113 /* FIXME - CFIndex same size as size_t on 64bit? */
1114 ortn
= SecCmsEncoderUpdate(cmsEncoder
->encoder
, content
, (CFIndex
)contentLen
);
1116 ortn
= cmsRtnToOSStatus(ortn
);
1117 CSSM_PERROR("SecCmsEncoderUpdate", ortn
);
1122 /* forward declaration */
1123 static OSStatus
CMSEncode(
1125 CFTypeRef recipients
,
1126 const SecAsn1Oid
*eContentType
,
1127 Boolean detachedContent
,
1128 CMSSignedAttributes signedAttributes
,
1129 const void *content
,
1131 CFDataRef
*encodedContent
);
1134 * Finish encoding the message and obtain the encoded result.
1135 * Caller must CFRelease the result.
1137 OSStatus
CMSEncoderCopyEncodedContent(
1138 CMSEncoderRef cmsEncoder
,
1139 CFDataRef
*encodedContent
)
1141 if((cmsEncoder
== NULL
) || (encodedContent
== NULL
)) {
1147 switch(cmsEncoder
->encState
) {
1149 /* normal termination */
1152 /* already been called */
1157 * The only time these are legal is when we're doing a SignedData
1158 * with certificates only (no signers, no content).
1160 if((cmsEncoder
->signers
!= NULL
) ||
1161 (cmsEncoder
->recipients
!= NULL
) ||
1162 (cmsEncoder
->otherCerts
== NULL
)) {
1166 /* Set up for certs only */
1167 ortn
= cmsSetupForSignedData(cmsEncoder
);
1171 /* and an encoder */
1172 ortn
= cmsSetupEncoder(cmsEncoder
);
1180 ASSERT(cmsEncoder
->encoder
!= NULL
);
1181 ortn
= SecCmsEncoderFinish(cmsEncoder
->encoder
);
1182 /* regardless of the outcome, the encoder itself has been freed */
1183 cmsEncoder
->encoder
= NULL
;
1185 return cmsRtnToOSStatus(ortn
);
1187 cmsEncoder
->encState
= ES_Final
;
1189 if((cmsEncoder
->encoderOut
== NULL
) && !cmsEncoder
->customCoder
) {
1190 /* not sure how this could happen... */
1191 dprintf("Successful encode, but no data\n");
1192 return errSecInternalComponent
;
1194 if(cmsEncoder
->customCoder
) {
1196 *encodedContent
= NULL
;
1197 return errSecSuccess
;
1200 /* in two out of three cases, we're done */
1201 switch(cmsEncoder
->op
) {
1204 *encodedContent
= CFDataCreateCopy(NULL
, cmsEncoder
->encoderOut
);
1205 return errSecSuccess
;
1206 case EO_SignEncrypt
:
1207 /* proceed, more work to do */
1212 * Signing & encrypting.
1213 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1214 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1215 * inside of the ContentInfo we just created as the data to encrypt.
1217 SecAsn1CoderRef asn1Coder
= NULL
;
1218 SecAsn1Item signedData
= {0, NULL
};
1220 ortn
= SecAsn1CoderCreate(&asn1Coder
);
1224 SecAsn1Item encoderOut
= { CFDataGetLength(cmsEncoder
->encoderOut
),
1225 CFDataGetMutableBytePtr(cmsEncoder
->encoderOut
)};
1226 ortn
= cmsContentInfoContent(asn1Coder
, &encoderOut
, &signedData
);
1231 /* now just encrypt that, one-shot */
1232 ortn
= CMSEncode(NULL
, /* no signers this time */
1233 cmsEncoder
->recipients
,
1234 &CSSMOID_PKCS7_SignedData
, /* fake out encoder so it doesn't try to actually
1235 * encode the signedData - this asserts the
1236 * SEC_OID_OTHER OID tag in the EnvelopedData's
1238 FALSE
, /* detachedContent */
1239 kCMSAttrNone
, /* signedAttributes - none this time */
1240 signedData
.Data
, signedData
.Length
,
1245 SecAsn1CoderRelease(asn1Coder
);
1250 #pragma mark --- High-level API ---
1253 * High-level, one-shot encoder function.
1255 static OSStatus
CMSEncode(
1257 CFTypeRef recipients
,
1258 const SecAsn1Oid
*eContentType
,
1259 Boolean detachedContent
,
1260 CMSSignedAttributes signedAttributes
,
1261 const void *content
,
1263 CFDataRef
*encodedContent
) /* RETURNED */
1265 if((signers
== NULL
) && (recipients
== NULL
)) {
1268 if(encodedContent
== NULL
) {
1272 CMSEncoderRef cmsEncoder
;
1275 /* set up the encoder */
1276 ortn
= CMSEncoderCreate(&cmsEncoder
);
1281 /* subsequent errors to errOut: */
1283 ortn
= CMSEncoderAddSigners(cmsEncoder
, signers
);
1289 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipients
);
1295 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1300 if(detachedContent
) {
1301 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
1306 if(signedAttributes
) {
1307 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, signedAttributes
);
1313 ortn
= CMSEncoderUpdateContent(cmsEncoder
, content
, contentLen
);
1317 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, encodedContent
);
1320 CFRelease(cmsEncoder
);
1324 OSStatus
CMSEncodeContent(
1326 CFTypeRef recipients
,
1327 CFTypeRef eContentTypeOID
,
1328 Boolean detachedContent
,
1329 CMSSignedAttributes signedAttributes
,
1330 const void *content
,
1332 CFDataRef
*encodedContentOut
) /* RETURNED */
1334 // convert eContentTypeOID to a CSSM_OID
1335 SecAsn1Oid contentType
= { 0, NULL
};
1336 if (eContentTypeOID
&& convertOid(eContentTypeOID
, &contentType
) != 0)
1338 const SecAsn1Oid
*contentTypePtr
= (eContentTypeOID
) ? &contentType
: NULL
;
1339 OSStatus result
= CMSEncode(signers
, recipients
, contentTypePtr
,
1340 detachedContent
, signedAttributes
,
1341 content
, contentLen
, encodedContentOut
);
1342 if (contentType
.Data
)
1343 free(contentType
.Data
);
1347 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1350 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1351 * If we don't have a SecCmsMessageRef yet, we create one now.
1352 * This is the only place where we go to state ES_Msg.
1354 OSStatus
CMSEncoderGetCmsMessage(
1355 CMSEncoderRef cmsEncoder
,
1356 SecCmsMessageRef
*cmsMessage
) /* RETURNED */
1358 if((cmsEncoder
== NULL
) || (cmsMessage
== NULL
)) {
1361 if(cmsEncoder
->cmsMsg
!= NULL
) {
1362 ASSERT(cmsEncoder
->encState
!= ES_Init
);
1363 *cmsMessage
= cmsEncoder
->cmsMsg
;
1364 return errSecSuccess
;
1367 OSStatus ortn
= cmsSetupCmsMsg(cmsEncoder
);
1371 *cmsMessage
= cmsEncoder
->cmsMsg
;
1373 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1374 cmsEncoder
->encState
= ES_Msg
;
1375 return errSecSuccess
;
1379 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1380 * If this is called, it must be called before the first call to
1381 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1382 * incoming SecCmsEncoderRef.
1384 OSStatus
CMSEncoderSetEncoder(
1385 CMSEncoderRef cmsEncoder
,
1386 SecCmsEncoderRef encoder
)
1388 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1394 switch(cmsEncoder
->encState
) {
1396 /* No message, no encoder */
1397 ASSERT(cmsEncoder
->cmsMsg
== NULL
);
1398 ASSERT(cmsEncoder
->encoder
== NULL
);
1399 ortn
= cmsSetupCmsMsg(cmsEncoder
);
1403 /* drop thru to set encoder */
1405 /* cmsMsg but no encoder */
1406 ASSERT(cmsEncoder
->cmsMsg
!= NULL
);
1407 ASSERT(cmsEncoder
->encoder
== NULL
);
1408 cmsEncoder
->encoder
= encoder
;
1409 cmsEncoder
->encState
= ES_Updating
;
1410 cmsEncoder
->customCoder
= true; /* we won't see data */
1411 return errSecSuccess
;
1413 /* no can do, too late */
1419 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1420 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1421 * CMSEncoderUpdateContent() has been called.
1422 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1424 OSStatus
CMSEncoderGetEncoder(
1425 CMSEncoderRef cmsEncoder
,
1426 SecCmsEncoderRef
*encoder
) /* RETURNED */
1428 if((cmsEncoder
== NULL
) || (encoder
== NULL
)) {
1432 /* any state, whether we have an encoder or not is OK */
1433 *encoder
= cmsEncoder
->encoder
;
1434 return errSecSuccess
;
1437 #if TIMESTAMPING_SUPPORTED
1438 #include <AssertMacros.h>
1440 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1441 * present. This timestamp is an authenticated timestamp provided by
1442 * a timestamping authority.
1444 * Returns errSecParam if the CMS message was not signed or if signerIndex
1445 * is greater than the number of signers of the message minus one.
1447 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1449 OSStatus
CMSEncoderCopySignerTimestamp(
1450 CMSEncoderRef cmsEncoder
,
1451 size_t signerIndex
, /* usually 0 */
1452 CFAbsoluteTime
*timestamp
) /* RETURNED */
1454 return CMSEncoderCopySignerTimestampWithPolicy(
1461 OSStatus
CMSEncoderCopySignerTimestampWithPolicy(
1462 CMSEncoderRef cmsEncoder
,
1463 CFTypeRef timeStampPolicy
,
1464 size_t signerIndex
, /* usually 0 */
1465 CFAbsoluteTime
*timestamp
) /* RETURNED */
1467 OSStatus status
= errSecParam
;
1468 SecCmsMessageRef cmsg
;
1469 SecCmsSignedDataRef signedData
= NULL
;
1470 int numContentInfos
= 0;
1472 require(cmsEncoder
&& timestamp
, xit
);
1473 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder
, &cmsg
), xit
);
1474 numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
1475 for (int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++)
1477 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
1478 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
1479 if (tag
== SEC_OID_PKCS7_SIGNED_DATA
)
1480 if ((signedData
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(ci
))) {
1481 SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, (int)signerIndex
);
1484 status
= SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo
, timeStampPolicy
, timestamp
);