1/*
2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * CMSEncoder.cpp - encode, sign, and/or encrypt CMS messages.
26 */
27
28#include "CMSEncoder.h"
29#include "CMSPrivate.h"
30#include "CMSUtils.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>
49#include <pthread.h>
50
51#include <security_smime/tsaSupport.h>
52#include <security_smime/cmspriv.h>
53
54#pragma mark --- Private types and definitions ---
55
56/*
57 * Encoder state.
58 */
59typedef enum {
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 */
65} CMSEncoderState;
66
67/*
68 * High-level operation: what are we doing?
69 */
70typedef enum {
71	EO_Sign,
72	EO_Encrypt,
73	EO_SignEncrypt
74} CMSEncoderOp;
75
76/*
77 * Caller's CMSEncoderRef points to one of these.
78 */
79struct _CMSEncoder {
80	CFRuntimeBase		base;
81	CMSEncoderState		encState;
82	CMSEncoderOp		op;
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;
97};
98
99static void cmsEncoderInit(CFTypeRef enc);
100static void cmsEncoderFinalize(CFTypeRef enc);
101
102static CFRuntimeClass cmsEncoderRuntimeClass =
103{
104	0,			/* version */
105	"CMSEncoder",
106	cmsEncoderInit,
107	NULL,		/* copy */
108	cmsEncoderFinalize,
109	NULL,		/* equal - just use pointer equality */
110	NULL,		/* hash, ditto */
111	NULL,		/* copyFormattingDesc */
112	NULL		/* copyDebugDesc */
113};
114
115void
116CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback);
117
118#pragma mark --- Private routines ---
119
120/*
121 * Decode a CFStringRef representation of an integer
122 */
123static int cfStringToNumber(
124	CFStringRef inStr)
125{
126	int max = 32;
127	char buf[max];
128	if (!inStr || !CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
129		return -1;
130	return atoi(buf);
131}
132
133/*
134 * Encode an integer component of an OID, return resulting number of bytes;
135 * actual bytes are mallocd and returned in *encodeArray.
136 */
137static unsigned encodeNumber(
138	int num,
139	unsigned char **encodeArray)		// mallocd and RETURNED
140{
141	unsigned char *result;
142	unsigned dex;
143	unsigned numDigits = 0;
144	unsigned scratch;
145
146	/* trival case - 0 maps to 0 */
147	if(num == 0) {
148		*encodeArray = (unsigned char *)malloc(1);
149		**encodeArray = 0;
150		return 1;
151	}
152
153	/* first calculate the number of digits in num, base 128 */
154	scratch = (unsigned)num;
155	while(scratch != 0) {
156		numDigits++;
157		scratch >>= 7;
158	}
159
160	result = (unsigned char *)malloc(numDigits);
161	scratch = (unsigned)num;
162	for(dex=0; dex<numDigits; dex++) {
163		result[numDigits - dex - 1] = scratch & 0x7f;
164		scratch >>= 7;
165	}
166
167	/* all digits except the last one have m.s. bit set */
168	for(dex=0; dex<(numDigits - 1); dex++) {
169		result[dex] |= 0x80;
170	}
171
172	*encodeArray = result;
173	return numDigits;
174}
175
176/*
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.
181 */
182static int encodeOid(
183	const unsigned char *inStr,
184	unsigned char **outOid,
185	unsigned int *outLen)
186{
187	unsigned char **digits = NULL;		/* array of char * from encodeNumber */
188	unsigned *numDigits = NULL;			/* array of unsigned from encodeNumber */
189	CFIndex digit;
190	unsigned numDigitBytes;				/* total #of output chars */
191	unsigned char firstByte;
192	unsigned char *outP;
193	CFIndex numsToProcess;
194	CFStringRef oidStr = NULL;
195	CFArrayRef argvRef = NULL;
196	int num, result = 1;
197        CFIndex argc;
198
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;
207
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;
214	firstByte += num;
215	numDigitBytes = 1;
216
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];
227		}
228	}
229	*outLen = (2 + numDigitBytes);
230	*outOid = outP = (unsigned char *) malloc(*outLen);
231	*outP++ = 0x06;
232	*outP++ = numDigitBytes;
233	*outP++ = firstByte;
234	for(digit=0; digit<numsToProcess; digit++) {
235		unsigned int byteDex;
236		for(byteDex=0; byteDex<numDigits[digit]; byteDex++) {
237			*outP++ = digits[digit][byteDex];
238		}
239	}
240	if(digits) {
241		for(digit=0; digit<numsToProcess; digit++) {
242			free(digits[digit]);
243		}
244		free(digits);
245		free(numDigits);
246	}
247	result = 0;
248
249cleanExit:
250	if (oidStr) CFRelease(oidStr);
251	if (argvRef) CFRelease(argvRef);
252
253	return result;
254}
255
256/*
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.
260 *
261 * Function returns 0 if successful, non-zero otherwise.
262 */
263
264static int convertOid(
265	CFTypeRef inRef,
266	CSSM_OID *outOid)
267{
268	if (!inRef || !outOid)
269		return errSecParam;
270
271	unsigned char *oidData = NULL;
272	unsigned int oidLen = 0;
273
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;
278		char buf[max];
279		if (!CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
280			return errSecParam;
281
282		if(encodeOid((unsigned char *)buf, &oidData, &oidLen) != 0)
283			return errSecParam;
284	}
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);
291	}
292	else {
293		// Not in a format we understand
294		return errSecParam;
295	}
296	outOid->Length = oidLen;
297	outOid->Data = (uint8 *)oidData;
298	return 0;
299}
300
301static CFTypeID cmsEncoderTypeID = _kCFRuntimeNotATypeID;
302
303/* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
304static void cmsEncoderClassInitialize(void)
305{
306	cmsEncoderTypeID =
307		_CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsEncoderRuntimeClass);
308}
309
310/* init called out from _CFRuntimeCreateInstance() */
311static void cmsEncoderInit(CFTypeRef enc)
312{
313	char *start = ((char *)enc) + sizeof(CFRuntimeBase);
314	memset(start, 0, sizeof(struct _CMSEncoder) - sizeof(CFRuntimeBase));
315}
316
317/*
318 * Dispose of a CMSEncoder. Called out from CFRelease().
319 */
320static void cmsEncoderFinalize(
321	CFTypeRef		enc)
322{
323	CMSEncoderRef cmsEncoder = (CMSEncoderRef)enc;
324	if(cmsEncoder == NULL) {
325		return;
326	}
327	if(cmsEncoder->eContentType.Data != NULL) {
328		free(cmsEncoder->eContentType.Data);
329	}
330	CFRELEASE(cmsEncoder->signers);
331	CFRELEASE(cmsEncoder->recipients);
332	CFRELEASE(cmsEncoder->otherCerts);
333	if(cmsEncoder->cmsMsg != NULL) {
334		SecCmsMessageDestroy(cmsEncoder->cmsMsg);
335	}
336	if(cmsEncoder->arena != NULL) {
337		SecArenaPoolFree(cmsEncoder->arena, false);
338	}
339	if(cmsEncoder->encoder != NULL) {
340		/*
341		 * Normally this gets freed in SecCmsEncoderFinish - this is
342		 * an error case.
343		 */
344		SecCmsEncoderDestroy(cmsEncoder->encoder);
345	}
346}
347
348static OSStatus cmsSetupEncoder(
349	CMSEncoderRef		cmsEncoder)
350{
351	OSStatus ortn;
352
353	ASSERT(cmsEncoder->arena == NULL);
354	ASSERT(cmsEncoder->encoder == NULL);
355
356	ortn = SecArenaPoolCreate(1024, &cmsEncoder->arena);
357	if(ortn) {
358		return cmsRtnToOSStatus(ortn);
359	}
360	ortn = SecCmsEncoderCreate(cmsEncoder->cmsMsg,
361		NULL, NULL,					// no callback
362		&cmsEncoder->encoderOut,	// data goes here
363		cmsEncoder->arena,
364		NULL, NULL,					// no password callback (right?)
365		NULL, NULL,					// decrypt key callback
366		NULL, NULL,					// detached digests
367		&cmsEncoder->encoder);
368	if(ortn) {
369		return cmsRtnToOSStatus(ortn);
370	}
371	return errSecSuccess;
372}
373
374/*
375 * Set up a SecCmsMessageRef for a SignedData creation.
376 */
377static OSStatus cmsSetupForSignedData(
378	CMSEncoderRef		cmsEncoder)
379{
380	ASSERT((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL));
381
382    SecCmsContentInfoRef contentInfo = NULL;
383    SecCmsSignedDataRef signedData = NULL;
384	OSStatus ortn;
385
386    /* build chain of objects: message->signedData->data */
387	if(cmsEncoder->cmsMsg != NULL) {
388		SecCmsMessageDestroy(cmsEncoder->cmsMsg);
389	}
390	cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
391	if(cmsEncoder->cmsMsg == NULL) {
392		return errSecInternalComponent;
393	}
394
395	signedData = SecCmsSignedDataCreate(cmsEncoder->cmsMsg);
396	if(signedData == NULL) {
397		return errSecInternalComponent;
398	}
399	contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
400	ortn = SecCmsContentInfoSetContentSignedData(cmsEncoder->cmsMsg, contentInfo,
401			signedData);
402	if(ortn) {
403		return cmsRtnToOSStatus(ortn);
404	}
405    contentInfo = SecCmsSignedDataGetContentInfo(signedData);
406	if(cmsEncoder->eContentType.Data != NULL) {
407		/* Override the default eContentType of id-data */
408		ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
409			contentInfo,
410			NULL,		/* data - provided to encoder, not here */
411			cmsEncoder->detachedContent,
412			&cmsEncoder->eContentType);
413	}
414	else {
415		ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
416			contentInfo,
417			NULL, /* data - provided to encoder, not here */
418			cmsEncoder->detachedContent);
419	}
420	if(ortn) {
421		ortn = cmsRtnToOSStatus(ortn);
422		CSSM_PERROR("SecCmsContentInfoSetContent*", ortn);
423		return ortn;
424	}
425
426	/* optional 'global' (per-SignedData) certs */
427	if(cmsEncoder->otherCerts != NULL) {
428		ortn = SecCmsSignedDataAddCertList(signedData, cmsEncoder->otherCerts);
429		if(ortn) {
430			ortn = cmsRtnToOSStatus(ortn);
431			CSSM_PERROR("SecCmsSignedDataAddCertList", ortn);
432			return ortn;
433		}
434	}
435
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);
441	}
442	CFIndex dex;
443	SecKeychainRef ourKc = NULL;
444	SecCertificateRef ourCert = NULL;
445	SecCmsCertChainMode chainMode = SecCmsCMCertChain;
446
447	switch(cmsEncoder->chainMode) {
448		case kCMSCertificateNone:
449			chainMode = SecCmsCMNone;
450			break;
451		case kCMSCertificateSignerOnly:
452			chainMode = SecCmsCMCertOnly;
453			break;
454		case kCMSCertificateChainWithRoot:
455			chainMode = SecCmsCMCertChainWithRoot;
456			break;
457		default:
458			break;
459	}
460	for(dex=0; dex<numSigners; dex++) {
461		SecCmsSignerInfoRef signerInfo;
462
463		SecIdentityRef ourId =
464			(SecIdentityRef)CFArrayGetValueAtIndex(cmsEncoder->signers, dex);
465		ortn = SecIdentityCopyCertificate(ourId, &ourCert);
466		if(ortn) {
467			CSSM_PERROR("SecIdentityCopyCertificate", ortn);
468			break;
469		}
470		ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
471		if(ortn) {
472			CSSM_PERROR("SecKeychainItemCopyKeychain", ortn);
473			break;
474		}
475		signerInfo = SecCmsSignerInfoCreate(cmsEncoder->cmsMsg, ourId, SEC_OID_SHA1);
476		if (signerInfo == NULL) {
477			ortn = errSecInternalComponent;
478			break;
479		}
480
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);
485		if(ortn) {
486			ortn = cmsRtnToOSStatus(ortn);
487			CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn);
488			break;
489		}
490
491		/* other options */
492		if(cmsEncoder->signedAttributes & kCMSAttrSmimeCapabilities) {
493			ortn = SecCmsSignerInfoAddSMIMECaps(signerInfo);
494			if(ortn) {
495				ortn = cmsRtnToOSStatus(ortn);
496				CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
497				break;
498			}
499		}
500		if(cmsEncoder->signedAttributes & kCMSAttrSmimeEncryptionKeyPrefs) {
501			ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
502			if(ortn) {
503				ortn = cmsRtnToOSStatus(ortn);
504				CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
505				break;
506			}
507		}
508		if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) {
509			ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
510			if(ortn) {
511				ortn = cmsRtnToOSStatus(ortn);
512				CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn);
513				break;
514			}
515		}
516		if(cmsEncoder->signedAttributes & kCMSAttrSigningTime) {
517			if (cmsEncoder->signingTime == 0)
518				cmsEncoder->signingTime = CFAbsoluteTimeGetCurrent();
519			ortn = SecCmsSignerInfoAddSigningTime(signerInfo, cmsEncoder->signingTime);
520			if(ortn) {
521				ortn = cmsRtnToOSStatus(ortn);
522				CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn);
523				break;
524			}
525		}
526
527		ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
528		if(ortn) {
529			ortn = cmsRtnToOSStatus(ortn);
530			CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn);
531			break;
532		}
533
534		CFRELEASE(ourKc);
535		CFRELEASE(ourCert);
536		ourKc = NULL;
537		ourCert = NULL;
538	}
539	if(ortn) {
540		CFRELEASE(ourKc);
541		CFRELEASE(ourCert);
542	}
543	return ortn;
544}
545
546/*
547 * Set up a SecCmsMessageRef for a EnvelopedData creation.
548 */
549static OSStatus cmsSetupForEnvelopedData(
550	CMSEncoderRef		cmsEncoder)
551{
552	ASSERT(cmsEncoder->op == EO_Encrypt);
553	ASSERT(cmsEncoder->recipients != NULL);
554
555    SecCmsContentInfoRef contentInfo = NULL;
556    SecCmsEnvelopedDataRef envelopedData = NULL;
557	SECOidTag algorithmTag;
558    int keySize;
559	OSStatus ortn;
560
561	/*
562	 * Find encryption algorithm...unfortunately we need a NULL-terminated array
563	 * of SecCertificateRefs for this.
564	 */
565	CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients);
566	CFIndex dex;
567	SecCertificateRef *certArray = (SecCertificateRef *)malloc(
568		(numCerts+1) * sizeof(SecCertificateRef));
569
570	for(dex=0; dex<numCerts; dex++) {
571		certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex(
572			cmsEncoder->recipients, dex);
573	}
574	certArray[numCerts] = NULL;
575	ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize);
576	free(certArray);
577	if(ortn) {
578		CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn);
579		return ortn;
580	}
581
582    /* build chain of objects: message->envelopedData->data */
583	if(cmsEncoder->cmsMsg != NULL) {
584		SecCmsMessageDestroy(cmsEncoder->cmsMsg);
585	}
586	cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
587	if(cmsEncoder->cmsMsg == NULL) {
588		return errSecInternalComponent;
589	}
590	envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg,
591		algorithmTag, keySize);
592	if(envelopedData == NULL) {
593		return errSecInternalComponent;
594	}
595	contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
596	ortn = SecCmsContentInfoSetContentEnvelopedData(cmsEncoder->cmsMsg,
597		contentInfo, envelopedData);
598	if(ortn) {
599		ortn = cmsRtnToOSStatus(ortn);
600		CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn);
601		return ortn;
602	}
603    contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
604	if(cmsEncoder->eContentType.Data != NULL) {
605		/* Override the default ContentType of id-data */
606		ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
607			contentInfo,
608			NULL,		/* data - provided to encoder, not here */
609			FALSE,		/* detachedContent */
610			&cmsEncoder->eContentType);
611	}
612	else {
613		ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
614			contentInfo,
615			NULL /* data - provided to encoder, not here */,
616			cmsEncoder->detachedContent);
617	}
618	if(ortn) {
619		ortn = cmsRtnToOSStatus(ortn);
620		CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn);
621		return ortn;
622	}
623
624    /*
625     * create & attach recipient information, one for each recipient
626     */
627	for(dex=0; dex<numCerts; dex++) {
628		SecCmsRecipientInfoRef recipientInfo = NULL;
629
630		SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex(
631			cmsEncoder->recipients, dex);
632		recipientInfo = SecCmsRecipientInfoCreate(cmsEncoder->cmsMsg, thisRecip);
633		ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
634		if(ortn) {
635			ortn = cmsRtnToOSStatus(ortn);
636			CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn);
637			return ortn;
638		}
639	}
640	return errSecSuccess;
641}
642
643/*
644 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
645 * from CMSEncodeGetCmsMessage().
646 */
647static OSStatus cmsSetupCmsMsg(
648	CMSEncoderRef		cmsEncoder)
649{
650	ASSERT(cmsEncoder != NULL);
651	ASSERT(cmsEncoder->encState == ES_Init);
652
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;
657		}
658		else {
659			cmsEncoder->op = EO_Sign;
660		}
661	}
662	else if(cmsEncoder->recipients != NULL) {
663		cmsEncoder->op = EO_Encrypt;
664	}
665	else {
666		dprintf("CMSEncoderUpdateContent: nothing to do\n");
667		return errSecParam;
668	}
669
670	OSStatus ortn = errSecSuccess;
671
672	switch(cmsEncoder->op) {
673		case EO_Sign:
674		case EO_SignEncrypt:
675			/* If we're signing & encrypting, do the signing first */
676			ortn = cmsSetupForSignedData(cmsEncoder);
677			break;
678		case EO_Encrypt:
679			ortn = cmsSetupForEnvelopedData(cmsEncoder);
680			break;
681	}
682	cmsEncoder->encState = ES_Msg;
683	return ortn;
684}
685
686/*
687 * ASN.1 template for decoding a ContentInfo.
688 */
689typedef struct {
690    CSSM_OID	contentType;
691    CSSM_DATA	content;
692} SimpleContentInfo;
693
694static 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 },
700    { 0, }
701};
702
703/*
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.
706 */
707static OSStatus cmsContentInfoContent(
708	SecAsn1CoderRef asn1Coder,
709	const CSSM_DATA *contentInfo,
710	CSSM_DATA *content)				/* RETURNED */
711{
712    OSStatus ortn;
713    SimpleContentInfo decodedInfo;
714
715    memset(&decodedInfo, 0, sizeof(decodedInfo));
716    ortn = SecAsn1DecodeData(asn1Coder, contentInfo,
717		cmsSimpleContentInfoTemplate, &decodedInfo);
718    if(ortn) {
719		return ortn;
720    }
721    if(decodedInfo.content.Data == NULL) {
722		dprintf("***Error decoding contentInfo: no content\n");
723		return errSecInternalComponent;
724    }
725    *content = decodedInfo.content;
726	return errSecSuccess;
727}
728
729#pragma mark --- Start of Public API ---
730
731CFTypeID CMSEncoderGetTypeID(void)
732{
733	static pthread_once_t once = PTHREAD_ONCE_INIT;
734
735	if(cmsEncoderTypeID == _kCFRuntimeNotATypeID) {
736		pthread_once(&once, &cmsEncoderClassInitialize);
737	}
738	return cmsEncoderTypeID;
739}
740
741/*
742 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
743 */
744OSStatus CMSEncoderCreate(
745	CMSEncoderRef		*cmsEncoderOut)	/* RETURNED */
746{
747	CMSEncoderRef cmsEncoder = NULL;
748
749	uint32_t extra = sizeof(*cmsEncoder) - sizeof(cmsEncoder->base);
750	cmsEncoder = (CMSEncoderRef)_CFRuntimeCreateInstance(NULL, CMSEncoderGetTypeID(),
751		extra, NULL);
752	if(cmsEncoder == NULL) {
753		return errSecAllocate;
754	}
755	cmsEncoder->encState = ES_Init;
756	cmsEncoder->chainMode = kCMSCertificateChain;
757	*cmsEncoderOut = cmsEncoder;
758	return errSecSuccess;
759}
760
761#pragma mark --- Getters & Setters ---
762
763/*
764 * Specify signers of the CMS message; implies that the message will be signed.
765 */
766OSStatus CMSEncoderAddSigners(
767	CMSEncoderRef		cmsEncoder,
768	CFTypeRef			signerOrArray)
769{
770	if(cmsEncoder == NULL) {
771		return errSecParam;
772	}
773	if(cmsEncoder->encState != ES_Init) {
774		return errSecParam;
775	}
776	return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID());
777}
778
779/*
780 * Obtain an array of signers as specified in CMSEncoderSetSigners().
781 */
782OSStatus CMSEncoderCopySigners(
783	CMSEncoderRef		cmsEncoder,
784	CFArrayRef			*signers)
785{
786	if((cmsEncoder == NULL) || (signers == NULL)) {
787		return errSecParam;
788	}
789	if(cmsEncoder->signers != NULL) {
790		CFRetain(cmsEncoder->signers);
791	}
792	*signers = cmsEncoder->signers;
793	return errSecSuccess;
794}
795
796/*
797 * Specify recipients of the message. Implies that the message will be encrypted.
798 */
799OSStatus CMSEncoderAddRecipients(
800	CMSEncoderRef		cmsEncoder,
801	CFTypeRef			recipientOrArray)
802{
803	if(cmsEncoder == NULL) {
804		return errSecParam;
805	}
806	if(cmsEncoder->encState != ES_Init) {
807		return errSecParam;
808	}
809	return cmsAppendToArray(recipientOrArray, &cmsEncoder->recipients,
810			SecCertificateGetTypeID());
811}
812
813/*
814 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
815 */
816OSStatus CMSEncoderCopyRecipients(
817	CMSEncoderRef		cmsEncoder,
818	CFArrayRef			*recipients)
819{
820	if((cmsEncoder == NULL) || (recipients == NULL)) {
821		return errSecParam;
822	}
823	if(cmsEncoder->recipients != NULL) {
824		CFRetain(cmsEncoder->recipients);
825	}
826	*recipients = cmsEncoder->recipients;
827	return errSecSuccess;
828}
829
830/*
831 * Specify additional certs to include in a signed message.
832 */
833OSStatus CMSEncoderAddSupportingCerts(
834	CMSEncoderRef		cmsEncoder,
835	CFTypeRef			certOrArray)
836{
837	if(cmsEncoder == NULL) {
838		return errSecParam;
839	}
840	if(cmsEncoder->encState != ES_Init) {
841		return errSecParam;
842	}
843	return cmsAppendToArray(certOrArray, &cmsEncoder->otherCerts,
844			SecCertificateGetTypeID());
845}
846
847/*
848 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
849 */
850OSStatus CMSEncoderCopySupportingCerts(
851	CMSEncoderRef		cmsEncoder,
852	CFArrayRef			*certs)			/* RETURNED */
853{
854	if((cmsEncoder == NULL) || (certs == NULL)) {
855		return errSecParam;
856	}
857	if(cmsEncoder->otherCerts != NULL) {
858		CFRetain(cmsEncoder->otherCerts);
859	}
860	*certs = cmsEncoder->otherCerts;
861	return errSecSuccess;
862}
863
864OSStatus CMSEncoderSetHasDetachedContent(
865	CMSEncoderRef		cmsEncoder,
866	Boolean				detachedContent)
867{
868	if(cmsEncoder == NULL) {
869		return errSecParam;
870	}
871	if(cmsEncoder->encState != ES_Init) {
872		return errSecParam;
873	}
874	cmsEncoder->detachedContent = detachedContent;
875	return errSecSuccess;
876}
877
878OSStatus CMSEncoderGetHasDetachedContent(
879	CMSEncoderRef		cmsEncoder,
880	Boolean				*detachedContent)	/* RETURNED */
881{
882	if((cmsEncoder == NULL) || (detachedContent == NULL)) {
883		return errSecParam;
884	}
885	*detachedContent = cmsEncoder->detachedContent;
886	return errSecSuccess;
887}
888
889/*
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.
893 */
894OSStatus CMSEncoderSetEncapsulatedContentType(
895	CMSEncoderRef		cmsEncoder,
896	const CSSM_OID	*eContentType)
897{
898	if((cmsEncoder == NULL) || (eContentType == NULL)) {
899		return errSecParam;
900	}
901	if(cmsEncoder->encState != ES_Init) {
902		return errSecParam;
903	}
904
905	CSSM_OID *ecOid = &cmsEncoder->eContentType;
906	if(ecOid->Data != NULL) {
907		free(ecOid->Data);
908	}
909	cmsCopyCmsData(eContentType, ecOid);
910	return errSecSuccess;
911}
912
913OSStatus CMSEncoderSetEncapsulatedContentTypeOID(
914	CMSEncoderRef		cmsEncoder,
915	CFTypeRef			eContentTypeOID)
916{
917	// convert eContentTypeOID to a CSSM_OID
918	CSSM_OID contentType = { 0, NULL };
919	if (!eContentTypeOID || convertOid(eContentTypeOID, &contentType) != 0)
920		return errSecParam;
921	OSStatus result = CMSEncoderSetEncapsulatedContentType(cmsEncoder, &contentType);
922	if (contentType.Data)
923		free(contentType.Data);
924	return result;
925}
926
927/*
928 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
929 */
930OSStatus CMSEncoderCopyEncapsulatedContentType(
931	CMSEncoderRef		cmsEncoder,
932	CFDataRef			*eContentType)
933{
934	if((cmsEncoder == NULL) || (eContentType == NULL)) {
935		return errSecParam;
936	}
937
938	CSSM_OID *ecOid = &cmsEncoder->eContentType;
939	if(ecOid->Data == NULL) {
940		*eContentType = NULL;
941	}
942	else {
943		*eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
944	}
945	return errSecSuccess;
946}
947
948/*
949 * Optionally specify signed attributes. Only meaningful when creating a
950 * signed message. If this is called, it must be called before
951 * CMSEncoderUpdateContent().
952 */
953OSStatus CMSEncoderAddSignedAttributes(
954	CMSEncoderRef		cmsEncoder,
955	CMSSignedAttributes	signedAttributes)
956{
957	if(cmsEncoder == NULL) {
958		return errSecParam;
959	}
960	if(cmsEncoder->encState != ES_Init) {
961		return errSecParam;
962	}
963	cmsEncoder->signedAttributes = signedAttributes;
964	return errSecSuccess;
965}
966
967/*
968 * Set the signing time for a CMSEncoder.
969 * This is only used if the kCMSAttrSigningTime attribute is included.
970 */
971OSStatus CMSEncoderSetSigningTime(
972	CMSEncoderRef		cmsEncoder,
973	CFAbsoluteTime		time)
974{
975	if(cmsEncoder == NULL) {
976		return errSecParam;
977	}
978	if(cmsEncoder->encState != ES_Init) {
979		return errSecParam;
980	}
981	cmsEncoder->signingTime = time;
982	return errSecSuccess;
983}
984
985
986OSStatus CMSEncoderSetCertificateChainMode(
987	CMSEncoderRef			cmsEncoder,
988	CMSCertificateChainMode	chainMode)
989{
990	if(cmsEncoder == NULL) {
991		return errSecParam;
992	}
993	if(cmsEncoder->encState != ES_Init) {
994		return errSecParam;
995	}
996	switch(chainMode) {
997		case kCMSCertificateNone:
998		case kCMSCertificateSignerOnly:
999		case kCMSCertificateChain:
1000		case kCMSCertificateChainWithRoot:
1001			break;
1002		default:
1003			return errSecParam;
1004	}
1005	cmsEncoder->chainMode = chainMode;
1006	return errSecSuccess;
1007}
1008
1009OSStatus CMSEncoderGetCertificateChainMode(
1010	CMSEncoderRef			cmsEncoder,
1011	CMSCertificateChainMode	*chainModeOut)
1012{
1013	if(cmsEncoder == NULL) {
1014		return errSecParam;
1015	}
1016	*chainModeOut = cmsEncoder->chainMode;
1017	return errSecSuccess;
1018}
1019
1020void
1021CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback)
1022{
1023    if (cmsEncoder->cmsMsg)
1024        SecCmsMessageSetTSACallback(cmsEncoder->cmsMsg, tsaCallback);
1025}
1026
1027void
1028CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder, CFTypeRef tsaContext)
1029{
1030    if (cmsEncoder->cmsMsg)
1031        SecCmsMessageSetTSAContext(cmsEncoder->cmsMsg, tsaContext);
1032}
1033
1034#pragma mark --- Action ---
1035
1036/*
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.
1040 */
1041OSStatus CMSEncoderUpdateContent(
1042	CMSEncoderRef		cmsEncoder,
1043	const void			*content,
1044	size_t				contentLen)
1045{
1046	if(cmsEncoder == NULL) {
1047		return errSecParam;
1048	}
1049
1050	OSStatus ortn = errSecSuccess;
1051	switch(cmsEncoder->encState) {
1052		case ES_Init:
1053			/*
1054			 * First time thru: do the CmsMsg setup.
1055			 */
1056			ortn = cmsSetupCmsMsg(cmsEncoder);
1057			if(ortn) {
1058				return ortn;
1059			}
1060			/* fall thru to set up the encoder */
1061
1062		case ES_Msg:
1063			/* We have a cmsMsg but no encoder; create one */
1064			ASSERT(cmsEncoder->cmsMsg != NULL);
1065			ASSERT(cmsEncoder->encoder == NULL);
1066			ortn = cmsSetupEncoder(cmsEncoder);
1067			if(ortn) {
1068				return ortn;
1069			}
1070			/* only legal calls now are update and finalize */
1071			cmsEncoder->encState = ES_Updating;
1072			break;
1073
1074		case ES_Updating:
1075			ASSERT(cmsEncoder->encoder != NULL);
1076			break;
1077
1078		case ES_Final:
1079			/* Too late for another update */
1080			return errSecParam;
1081
1082		default:
1083			return errSecInternalComponent;
1084	}
1085
1086	/* FIXME - CFIndex same size as size_t on 64bit? */
1087	ortn = SecCmsEncoderUpdate(cmsEncoder->encoder, content, (CFIndex)contentLen);
1088	if(ortn) {
1089		ortn = cmsRtnToOSStatus(ortn);
1090		CSSM_PERROR("SecCmsEncoderUpdate", ortn);
1091	}
1092	return ortn;
1093}
1094
1095/*
1096 * Finish encoding the message and obtain the encoded result.
1097 * Caller must CFRelease the result.
1098 */
1099OSStatus CMSEncoderCopyEncodedContent(
1100	CMSEncoderRef		cmsEncoder,
1101	CFDataRef			*encodedContent)
1102{
1103	if((cmsEncoder == NULL) || (encodedContent == NULL)) {
1104		return errSecParam;
1105	}
1106
1107	OSStatus ortn;
1108
1109	switch(cmsEncoder->encState) {
1110		case ES_Updating:
1111			/* normal termination */
1112			break;
1113		case ES_Final:
1114			/* already been called */
1115			return errSecParam;
1116		case ES_Msg:
1117		case ES_Init:
1118			/*
1119			 * The only time these are legal is when we're doing a SignedData
1120			 * with certificates only (no signers, no content).
1121			 */
1122			if((cmsEncoder->signers != NULL) ||
1123			   (cmsEncoder->recipients != NULL) ||
1124			   (cmsEncoder->otherCerts == NULL)) {
1125				return errSecParam;
1126			}
1127
1128			/* Set up for certs only */
1129			ortn = cmsSetupForSignedData(cmsEncoder);
1130			if(ortn) {
1131				return ortn;
1132			}
1133			/* and an encoder */
1134			ortn = cmsSetupEncoder(cmsEncoder);
1135			if(ortn) {
1136				return ortn;
1137			}
1138			break;
1139	}
1140
1141
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;
1146	if(ortn) {
1147		return cmsRtnToOSStatus(ortn);
1148	}
1149	cmsEncoder->encState = ES_Final;
1150
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;
1155	}
1156	if(cmsEncoder->customCoder) {
1157		/* we're done */
1158		*encodedContent = NULL;
1159		return errSecSuccess;
1160	}
1161
1162	/* in two out of three cases, we're done */
1163	switch(cmsEncoder->op) {
1164		case EO_Sign:
1165		case EO_Encrypt:
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 */
1171			break;
1172	}
1173
1174	/*
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.
1179	 */
1180	SecAsn1CoderRef asn1Coder = NULL;
1181	CSSM_DATA signedData = {0, NULL};
1182
1183	ortn = SecAsn1CoderCreate(&asn1Coder);
1184	if(ortn) {
1185		return ortn;
1186	}
1187	ortn = cmsContentInfoContent(asn1Coder, &cmsEncoder->encoderOut, &signedData);
1188	if(ortn) {
1189		goto errOut;
1190	}
1191
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
1198									 *   ContentInfo */
1199		FALSE,						/* detachedContent */
1200		kCMSAttrNone,				/* signedAttributes - none this time */
1201		signedData.Data, signedData.Length,
1202		encodedContent);
1203
1204errOut:
1205	if(asn1Coder) {
1206		SecAsn1CoderRelease(asn1Coder);
1207	}
1208	return ortn;
1209}
1210
1211#pragma mark --- High-level API ---
1212
1213/*
1214 * High-level, one-shot encoder function.
1215 */
1216OSStatus CMSEncode(
1217	CFTypeRef			signers,
1218	CFTypeRef			recipients,
1219	const CSSM_OID		*eContentType,
1220	Boolean				detachedContent,
1221	CMSSignedAttributes	signedAttributes,
1222	const void			*content,
1223	size_t				contentLen,
1224	CFDataRef			*encodedContent)	/* RETURNED */
1225{
1226	if((signers == NULL) && (recipients == NULL)) {
1227		return errSecParam;
1228	}
1229	if(encodedContent == NULL) {
1230		return errSecParam;
1231	}
1232
1233	CMSEncoderRef cmsEncoder;
1234	OSStatus ortn;
1235
1236	/* set up the encoder */
1237	ortn = CMSEncoderCreate(&cmsEncoder);
1238	if(ortn) {
1239		return ortn;
1240	}
1241
1242	/* subsequent errors to errOut: */
1243	if(signers) {
1244		ortn = CMSEncoderAddSigners(cmsEncoder, signers);
1245		if(ortn) {
1246			goto errOut;
1247		}
1248	}
1249	if(recipients) {
1250		ortn = CMSEncoderAddRecipients(cmsEncoder, recipients);
1251		if(ortn) {
1252			goto errOut;
1253		}
1254	}
1255	if(eContentType) {
1256		ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
1257		if(ortn) {
1258			goto errOut;
1259		}
1260	}
1261	if(detachedContent) {
1262		ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
1263		if(ortn) {
1264			goto errOut;
1265		}
1266	}
1267	if(signedAttributes) {
1268		ortn = CMSEncoderAddSignedAttributes(cmsEncoder, signedAttributes);
1269		if(ortn) {
1270			goto errOut;
1271		}
1272	}
1273	/* GO */
1274	ortn = CMSEncoderUpdateContent(cmsEncoder, content, contentLen);
1275	if(ortn) {
1276		goto errOut;
1277	}
1278	ortn = CMSEncoderCopyEncodedContent(cmsEncoder, encodedContent);
1279
1280errOut:
1281	CFRelease(cmsEncoder);
1282	return ortn;
1283}
1284
1285OSStatus CMSEncodeContent(
1286	CFTypeRef			signers,
1287	CFTypeRef			recipients,
1288	CFTypeRef			eContentTypeOID,
1289	Boolean				detachedContent,
1290	CMSSignedAttributes	signedAttributes,
1291	const void			*content,
1292	size_t				contentLen,
1293	CFDataRef			*encodedContentOut)	/* RETURNED */
1294{
1295	// convert eContentTypeOID to a CSSM_OID
1296	CSSM_OID contentType = { 0, NULL };
1297	if (eContentTypeOID && convertOid(eContentTypeOID, &contentType) != 0)
1298		return errSecParam;
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);
1305	return result;
1306}
1307
1308#pragma mark --- SPI routines declared in CMSPrivate.h ---
1309
1310/*
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.
1314 */
1315OSStatus CMSEncoderGetCmsMessage(
1316	CMSEncoderRef		cmsEncoder,
1317	SecCmsMessageRef	*cmsMessage)		/* RETURNED */
1318{
1319	if((cmsEncoder == NULL) || (cmsMessage == NULL)) {
1320		return errSecParam;
1321	}
1322	if(cmsEncoder->cmsMsg != NULL) {
1323		ASSERT(cmsEncoder->encState != ES_Init);
1324		*cmsMessage = cmsEncoder->cmsMsg;
1325		return errSecSuccess;
1326	}
1327
1328	OSStatus ortn = cmsSetupCmsMsg(cmsEncoder);
1329	if(ortn) {
1330		return ortn;
1331	}
1332	*cmsMessage = cmsEncoder->cmsMsg;
1333
1334	/* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1335	cmsEncoder->encState = ES_Msg;
1336	return errSecSuccess;
1337}
1338
1339/*
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.
1344 */
1345OSStatus CMSEncoderSetEncoder(
1346	CMSEncoderRef		cmsEncoder,
1347	SecCmsEncoderRef	encoder)
1348{
1349	if((cmsEncoder == NULL) || (encoder == NULL)) {
1350		return errSecParam;
1351	}
1352
1353	OSStatus ortn;
1354
1355	switch(cmsEncoder->encState) {
1356		case ES_Init:
1357			/* No message, no encoder */
1358			ASSERT(cmsEncoder->cmsMsg == NULL);
1359			ASSERT(cmsEncoder->encoder == NULL);
1360			ortn = cmsSetupCmsMsg(cmsEncoder);
1361			if(ortn) {
1362				return ortn;
1363			}
1364			/* drop thru to set encoder */
1365		case ES_Msg:
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;
1373		default:
1374			/* no can do, too late */
1375			return errSecParam;
1376	}
1377}
1378
1379/*
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.
1384 */
1385OSStatus CMSEncoderGetEncoder(
1386	CMSEncoderRef		cmsEncoder,
1387	SecCmsEncoderRef	*encoder)			/* RETURNED */
1388{
1389	if((cmsEncoder == NULL) || (encoder == NULL)) {
1390		return errSecParam;
1391	}
1392
1393	/* any state, whether we have an encoder or not is OK */
1394	*encoder = cmsEncoder->encoder;
1395	return errSecSuccess;
1396}
1397
1398#include <AssertMacros.h>
1399
1400/*
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.
1404 *
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.
1407 *
1408 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1409 */
1410OSStatus CMSEncoderCopySignerTimestamp(
1411                                       CMSEncoderRef		cmsEncoder,
1412                                       size_t				signerIndex,        /* usually 0 */
1413                                       CFAbsoluteTime      *timestamp)			/* RETURNED */
1414{
1415    return CMSEncoderCopySignerTimestampWithPolicy(
1416                                       cmsEncoder,
1417                                       NULL,
1418                                       signerIndex,
1419                                       timestamp);
1420}
1421
1422OSStatus CMSEncoderCopySignerTimestampWithPolicy(
1423	CMSEncoderRef		cmsEncoder,
1424    CFTypeRef            timeStampPolicy,
1425	size_t				signerIndex,        /* usually 0 */
1426	CFAbsoluteTime      *timestamp)			/* RETURNED */
1427{
1428    OSStatus status = errSecParam;
1429	SecCmsMessageRef cmsg;
1430	SecCmsSignedDataRef signedData = NULL;
1431    int numContentInfos = 0;
1432
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++)
1437    {
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))
1443                {
1444                    status = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp);
1445                    break;
1446                }
1447    }
1448
1449xit:
1450    return status;
1451}
1452