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