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