]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_cms/lib/CMSEncoder.cpp
Security-57337.60.2.tar.gz
[apple/security.git] / OSX / libsecurity_cms / lib / CMSEncoder.cpp
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 #include <utilities/SecCFRelease.h>
51
52 #include <security_smime/tsaSupport.h>
53 #include <security_smime/cmspriv.h>
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 CSSM_OID eContentType;
86 CFMutableArrayRef signers;
87 CFMutableArrayRef recipients;
88 CFMutableArrayRef otherCerts;
89 CMSSignedAttributes signedAttributes;
90 CFAbsoluteTime signingTime;
91 SecCmsMessageRef cmsMsg;
92 SecArenaPoolRef arena; /* the encoder's arena */
93 SecCmsEncoderRef encoder;
94 CSSM_DATA encoderOut; /* output goes here... */
95 bool customCoder; /* unless this is set by
96 * CMSEncoderSetEncoder */
97 SECOidTag digestalgtag;
98
99 CMSCertificateChainMode chainMode;
100 CFDataRef hashAgilityAttrValue;
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 void
120 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback);
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 free(digits);
249 free(numDigits);
250 }
251 result = 0;
252
253 cleanExit:
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 CSSM_OID *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 *)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 }
340 if(cmsEncoder->arena != NULL) {
341 SecArenaPoolFree(cmsEncoder->arena, false);
342 }
343 if(cmsEncoder->encoder != NULL) {
344 /*
345 * Normally this gets freed in SecCmsEncoderFinish - this is
346 * an error case.
347 */
348 SecCmsEncoderDestroy(cmsEncoder->encoder);
349 }
350 }
351
352 static OSStatus cmsSetupEncoder(
353 CMSEncoderRef cmsEncoder)
354 {
355 OSStatus ortn;
356
357 ASSERT(cmsEncoder->arena == NULL);
358 ASSERT(cmsEncoder->encoder == NULL);
359
360 ortn = SecArenaPoolCreate(1024, &cmsEncoder->arena);
361 if(ortn) {
362 return cmsRtnToOSStatus(ortn);
363 }
364 ortn = SecCmsEncoderCreate(cmsEncoder->cmsMsg,
365 NULL, NULL, // no callback
366 &cmsEncoder->encoderOut, // data goes here
367 cmsEncoder->arena,
368 NULL, NULL, // no password callback (right?)
369 NULL, NULL, // decrypt key callback
370 NULL, NULL, // detached digests
371 &cmsEncoder->encoder);
372 if(ortn) {
373 return cmsRtnToOSStatus(ortn);
374 }
375 return errSecSuccess;
376 }
377
378 /*
379 * Set up a SecCmsMessageRef for a SignedData creation.
380 */
381 static OSStatus cmsSetupForSignedData(
382 CMSEncoderRef cmsEncoder)
383 {
384 ASSERT((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL));
385
386 SecCmsContentInfoRef contentInfo = NULL;
387 SecCmsSignedDataRef signedData = NULL;
388 OSStatus ortn;
389
390 /* build chain of objects: message->signedData->data */
391 if(cmsEncoder->cmsMsg != NULL) {
392 SecCmsMessageDestroy(cmsEncoder->cmsMsg);
393 }
394 cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
395 if(cmsEncoder->cmsMsg == NULL) {
396 return errSecInternalComponent;
397 }
398
399 signedData = SecCmsSignedDataCreate(cmsEncoder->cmsMsg);
400 if(signedData == NULL) {
401 return errSecInternalComponent;
402 }
403 contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
404 ortn = SecCmsContentInfoSetContentSignedData(cmsEncoder->cmsMsg, contentInfo,
405 signedData);
406 if(ortn) {
407 return cmsRtnToOSStatus(ortn);
408 }
409 contentInfo = SecCmsSignedDataGetContentInfo(signedData);
410 if(cmsEncoder->eContentType.Data != NULL) {
411 /* Override the default eContentType of id-data */
412 ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
413 contentInfo,
414 NULL, /* data - provided to encoder, not here */
415 cmsEncoder->detachedContent,
416 &cmsEncoder->eContentType);
417 }
418 else {
419 ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
420 contentInfo,
421 NULL, /* data - provided to encoder, not here */
422 cmsEncoder->detachedContent);
423 }
424 if(ortn) {
425 ortn = cmsRtnToOSStatus(ortn);
426 CSSM_PERROR("SecCmsContentInfoSetContent*", ortn);
427 return ortn;
428 }
429
430 /* optional 'global' (per-SignedData) certs */
431 if(cmsEncoder->otherCerts != NULL) {
432 ortn = SecCmsSignedDataAddCertList(signedData, cmsEncoder->otherCerts);
433 if(ortn) {
434 ortn = cmsRtnToOSStatus(ortn);
435 CSSM_PERROR("SecCmsSignedDataAddCertList", ortn);
436 return ortn;
437 }
438 }
439
440 /* SignerInfos, one per signer */
441 CFIndex numSigners = 0;
442 if(cmsEncoder->signers != NULL) {
443 /* this is optional...in case we're just creating a cert bundle */
444 numSigners = CFArrayGetCount(cmsEncoder->signers);
445 }
446 CFIndex dex;
447 SecKeychainRef ourKc = NULL;
448 SecCertificateRef ourCert = NULL;
449 SecCmsCertChainMode chainMode = SecCmsCMCertChain;
450
451 switch(cmsEncoder->chainMode) {
452 case kCMSCertificateNone:
453 chainMode = SecCmsCMNone;
454 break;
455 case kCMSCertificateSignerOnly:
456 chainMode = SecCmsCMCertOnly;
457 break;
458 case kCMSCertificateChainWithRoot:
459 chainMode = SecCmsCMCertChainWithRoot;
460 break;
461 default:
462 break;
463 }
464 for(dex=0; dex<numSigners; dex++) {
465 SecCmsSignerInfoRef signerInfo;
466
467 SecIdentityRef ourId =
468 (SecIdentityRef)CFArrayGetValueAtIndex(cmsEncoder->signers, dex);
469 ortn = SecIdentityCopyCertificate(ourId, &ourCert);
470 if(ortn) {
471 CSSM_PERROR("SecIdentityCopyCertificate", ortn);
472 break;
473 }
474 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
475 if(ortn) {
476 CSSM_PERROR("SecKeychainItemCopyKeychain", ortn);
477 break;
478 }
479 signerInfo = SecCmsSignerInfoCreate(cmsEncoder->cmsMsg, ourId, cmsEncoder->digestalgtag);
480 if (signerInfo == NULL) {
481 ortn = errSecInternalComponent;
482 break;
483 }
484
485 /* we want the cert chain included for this one */
486 /* NOTE the usage parameter is currently unused by the SMIME lib */
487 ortn = SecCmsSignerInfoIncludeCerts(signerInfo, chainMode,
488 certUsageEmailSigner);
489 if(ortn) {
490 ortn = cmsRtnToOSStatus(ortn);
491 CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn);
492 break;
493 }
494
495 /* other options */
496 if(cmsEncoder->signedAttributes & kCMSAttrSmimeCapabilities) {
497 ortn = SecCmsSignerInfoAddSMIMECaps(signerInfo);
498 if(ortn) {
499 ortn = cmsRtnToOSStatus(ortn);
500 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
501 break;
502 }
503 }
504 if(cmsEncoder->signedAttributes & kCMSAttrSmimeEncryptionKeyPrefs) {
505 ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
506 if(ortn) {
507 ortn = cmsRtnToOSStatus(ortn);
508 CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
509 break;
510 }
511 }
512 if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) {
513 ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
514 if(ortn) {
515 ortn = cmsRtnToOSStatus(ortn);
516 CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn);
517 break;
518 }
519 }
520 if(cmsEncoder->signedAttributes & kCMSAttrSigningTime) {
521 if (cmsEncoder->signingTime == 0)
522 cmsEncoder->signingTime = CFAbsoluteTimeGetCurrent();
523 ortn = SecCmsSignerInfoAddSigningTime(signerInfo, cmsEncoder->signingTime);
524 if(ortn) {
525 ortn = cmsRtnToOSStatus(ortn);
526 CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn);
527 break;
528 }
529 }
530 if(cmsEncoder->signedAttributes & kCMSAttrAppleCodesigningHashAgility) {
531 ortn = SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo, cmsEncoder->hashAgilityAttrValue);
532 /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
533 CFReleaseNull(cmsEncoder->hashAgilityAttrValue);
534 if(ortn) {
535 ortn = cmsRtnToOSStatus(ortn);
536 CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn);
537 break;
538 }
539 }
540
541 ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
542 if(ortn) {
543 ortn = cmsRtnToOSStatus(ortn);
544 CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn);
545 break;
546 }
547
548 CFRELEASE(ourKc);
549 CFRELEASE(ourCert);
550 ourKc = NULL;
551 ourCert = NULL;
552 }
553 if(ortn) {
554 CFRELEASE(ourKc);
555 CFRELEASE(ourCert);
556 }
557 return ortn;
558 }
559
560 /*
561 * Set up a SecCmsMessageRef for a EnvelopedData creation.
562 */
563 static OSStatus cmsSetupForEnvelopedData(
564 CMSEncoderRef cmsEncoder)
565 {
566 ASSERT(cmsEncoder->op == EO_Encrypt);
567 ASSERT(cmsEncoder->recipients != NULL);
568
569 SecCmsContentInfoRef contentInfo = NULL;
570 SecCmsEnvelopedDataRef envelopedData = NULL;
571 SECOidTag algorithmTag;
572 int keySize;
573 OSStatus ortn;
574
575 /*
576 * Find encryption algorithm...unfortunately we need a NULL-terminated array
577 * of SecCertificateRefs for this.
578 */
579 CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients);
580 CFIndex dex;
581 SecCertificateRef *certArray = (SecCertificateRef *)malloc(
582 (numCerts+1) * sizeof(SecCertificateRef));
583
584 for(dex=0; dex<numCerts; dex++) {
585 certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex(
586 cmsEncoder->recipients, dex);
587 }
588 certArray[numCerts] = NULL;
589 ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize);
590 free(certArray);
591 if(ortn) {
592 CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn);
593 return ortn;
594 }
595
596 /* build chain of objects: message->envelopedData->data */
597 if(cmsEncoder->cmsMsg != NULL) {
598 SecCmsMessageDestroy(cmsEncoder->cmsMsg);
599 }
600 cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
601 if(cmsEncoder->cmsMsg == NULL) {
602 return errSecInternalComponent;
603 }
604 envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg,
605 algorithmTag, keySize);
606 if(envelopedData == NULL) {
607 return errSecInternalComponent;
608 }
609 contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
610 ortn = SecCmsContentInfoSetContentEnvelopedData(cmsEncoder->cmsMsg,
611 contentInfo, envelopedData);
612 if(ortn) {
613 ortn = cmsRtnToOSStatus(ortn);
614 CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn);
615 return ortn;
616 }
617 contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
618 if(cmsEncoder->eContentType.Data != NULL) {
619 /* Override the default ContentType of id-data */
620 ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
621 contentInfo,
622 NULL, /* data - provided to encoder, not here */
623 FALSE, /* detachedContent */
624 &cmsEncoder->eContentType);
625 }
626 else {
627 ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
628 contentInfo,
629 NULL /* data - provided to encoder, not here */,
630 cmsEncoder->detachedContent);
631 }
632 if(ortn) {
633 ortn = cmsRtnToOSStatus(ortn);
634 CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn);
635 return ortn;
636 }
637
638 /*
639 * create & attach recipient information, one for each recipient
640 */
641 for(dex=0; dex<numCerts; dex++) {
642 SecCmsRecipientInfoRef recipientInfo = NULL;
643
644 SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex(
645 cmsEncoder->recipients, dex);
646 recipientInfo = SecCmsRecipientInfoCreate(cmsEncoder->cmsMsg, thisRecip);
647 ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
648 if(ortn) {
649 ortn = cmsRtnToOSStatus(ortn);
650 CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn);
651 return ortn;
652 }
653 }
654 return errSecSuccess;
655 }
656
657 /*
658 * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
659 * from CMSEncodeGetCmsMessage().
660 */
661 static OSStatus cmsSetupCmsMsg(
662 CMSEncoderRef cmsEncoder)
663 {
664 ASSERT(cmsEncoder != NULL);
665 ASSERT(cmsEncoder->encState == ES_Init);
666
667 /* figure out what high-level operation we're doing */
668 if((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL)) {
669 if(cmsEncoder->recipients != NULL) {
670 cmsEncoder->op = EO_SignEncrypt;
671 }
672 else {
673 cmsEncoder->op = EO_Sign;
674 }
675 }
676 else if(cmsEncoder->recipients != NULL) {
677 cmsEncoder->op = EO_Encrypt;
678 }
679 else {
680 dprintf("CMSEncoderUpdateContent: nothing to do\n");
681 return errSecParam;
682 }
683
684 OSStatus ortn = errSecSuccess;
685
686 switch(cmsEncoder->op) {
687 case EO_Sign:
688 case EO_SignEncrypt:
689 /* If we're signing & encrypting, do the signing first */
690 ortn = cmsSetupForSignedData(cmsEncoder);
691 break;
692 case EO_Encrypt:
693 ortn = cmsSetupForEnvelopedData(cmsEncoder);
694 break;
695 }
696 cmsEncoder->encState = ES_Msg;
697 return ortn;
698 }
699
700 /*
701 * ASN.1 template for decoding a ContentInfo.
702 */
703 typedef struct {
704 CSSM_OID contentType;
705 CSSM_DATA content;
706 } SimpleContentInfo;
707
708 static const SecAsn1Template cmsSimpleContentInfoTemplate[] = {
709 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) },
710 { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) },
711 { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
712 offsetof(SimpleContentInfo, content),
713 kSecAsn1AnyTemplate },
714 { 0, }
715 };
716
717 /*
718 * Obtain the content of a contentInfo, This basically strips off the contentType OID
719 * and returns its ASN_ANY content, allocated the provided coder's memory space.
720 */
721 static OSStatus cmsContentInfoContent(
722 SecAsn1CoderRef asn1Coder,
723 const CSSM_DATA *contentInfo,
724 CSSM_DATA *content) /* RETURNED */
725 {
726 OSStatus ortn;
727 SimpleContentInfo decodedInfo;
728
729 memset(&decodedInfo, 0, sizeof(decodedInfo));
730 ortn = SecAsn1DecodeData(asn1Coder, contentInfo,
731 cmsSimpleContentInfoTemplate, &decodedInfo);
732 if(ortn) {
733 return ortn;
734 }
735 if(decodedInfo.content.Data == NULL) {
736 dprintf("***Error decoding contentInfo: no content\n");
737 return errSecInternalComponent;
738 }
739 *content = decodedInfo.content;
740 return errSecSuccess;
741 }
742
743 #pragma mark --- Start of Public API ---
744
745 CFTypeID CMSEncoderGetTypeID(void)
746 {
747 static pthread_once_t once = PTHREAD_ONCE_INIT;
748
749 if(cmsEncoderTypeID == _kCFRuntimeNotATypeID) {
750 pthread_once(&once, &cmsEncoderClassInitialize);
751 }
752 return cmsEncoderTypeID;
753 }
754
755 /*
756 * Create a CMSEncoder. Result must eventually be freed via CFRelease().
757 */
758 OSStatus CMSEncoderCreate(
759 CMSEncoderRef *cmsEncoderOut) /* RETURNED */
760 {
761 CMSEncoderRef cmsEncoder = NULL;
762
763 uint32_t extra = sizeof(*cmsEncoder) - sizeof(cmsEncoder->base);
764 cmsEncoder = (CMSEncoderRef)_CFRuntimeCreateInstance(NULL, CMSEncoderGetTypeID(),
765 extra, NULL);
766 if(cmsEncoder == NULL) {
767 return errSecAllocate;
768 }
769 cmsEncoder->encState = ES_Init;
770 cmsEncoder->chainMode = kCMSCertificateChain;
771 cmsEncoder->digestalgtag = SEC_OID_SHA1;
772 *cmsEncoderOut = cmsEncoder;
773 return errSecSuccess;
774 }
775
776 #pragma mark --- Getters & Setters ---
777
778 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA1 = CFSTR("sha1");
779 const CFStringRef __nonnull kCMSEncoderDigestAlgorithmSHA256 = CFSTR("sha256");
780
781
782 OSStatus CMSEncoderSetSignerAlgorithm(
783 CMSEncoderRef cmsEncoder,
784 CFStringRef digestAlgorithm)
785 {
786 if (CFEqual(digestAlgorithm, kCMSEncoderDigestAlgorithmSHA1)) {
787 cmsEncoder->digestalgtag = SEC_OID_SHA1;
788 } else if (CFEqual(digestAlgorithm, kCMSEncoderDigestAlgorithmSHA256)) {
789 cmsEncoder->digestalgtag = SEC_OID_SHA256;
790 } else {
791 return errSecParam;
792 }
793
794 return errSecSuccess;
795 }
796
797 /*
798 * Specify signers of the CMS message; implies that the message will be signed.
799 */
800 OSStatus CMSEncoderAddSigners(
801 CMSEncoderRef cmsEncoder,
802 CFTypeRef signerOrArray)
803 {
804 if(cmsEncoder == NULL) {
805 return errSecParam;
806 }
807 if(cmsEncoder->encState != ES_Init) {
808 return errSecParam;
809 }
810 return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID());
811 }
812
813 /*
814 * Obtain an array of signers as specified in CMSEncoderSetSigners().
815 */
816 OSStatus CMSEncoderCopySigners(
817 CMSEncoderRef cmsEncoder,
818 CFArrayRef *signers)
819 {
820 if((cmsEncoder == NULL) || (signers == NULL)) {
821 return errSecParam;
822 }
823 if(cmsEncoder->signers != NULL) {
824 CFRetain(cmsEncoder->signers);
825 }
826 *signers = cmsEncoder->signers;
827 return errSecSuccess;
828 }
829
830 /*
831 * Specify recipients of the message. Implies that the message will be encrypted.
832 */
833 OSStatus CMSEncoderAddRecipients(
834 CMSEncoderRef cmsEncoder,
835 CFTypeRef recipientOrArray)
836 {
837 if(cmsEncoder == NULL) {
838 return errSecParam;
839 }
840 if(cmsEncoder->encState != ES_Init) {
841 return errSecParam;
842 }
843 return cmsAppendToArray(recipientOrArray, &cmsEncoder->recipients,
844 SecCertificateGetTypeID());
845 }
846
847 /*
848 * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
849 */
850 OSStatus CMSEncoderCopyRecipients(
851 CMSEncoderRef cmsEncoder,
852 CFArrayRef *recipients)
853 {
854 if((cmsEncoder == NULL) || (recipients == NULL)) {
855 return errSecParam;
856 }
857 if(cmsEncoder->recipients != NULL) {
858 CFRetain(cmsEncoder->recipients);
859 }
860 *recipients = cmsEncoder->recipients;
861 return errSecSuccess;
862 }
863
864 /*
865 * Specify additional certs to include in a signed message.
866 */
867 OSStatus CMSEncoderAddSupportingCerts(
868 CMSEncoderRef cmsEncoder,
869 CFTypeRef certOrArray)
870 {
871 if(cmsEncoder == NULL) {
872 return errSecParam;
873 }
874 if(cmsEncoder->encState != ES_Init) {
875 return errSecParam;
876 }
877 return cmsAppendToArray(certOrArray, &cmsEncoder->otherCerts,
878 SecCertificateGetTypeID());
879 }
880
881 /*
882 * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
883 */
884 OSStatus CMSEncoderCopySupportingCerts(
885 CMSEncoderRef cmsEncoder,
886 CFArrayRef *certs) /* RETURNED */
887 {
888 if((cmsEncoder == NULL) || (certs == NULL)) {
889 return errSecParam;
890 }
891 if(cmsEncoder->otherCerts != NULL) {
892 CFRetain(cmsEncoder->otherCerts);
893 }
894 *certs = cmsEncoder->otherCerts;
895 return errSecSuccess;
896 }
897
898 OSStatus CMSEncoderSetHasDetachedContent(
899 CMSEncoderRef cmsEncoder,
900 Boolean detachedContent)
901 {
902 if(cmsEncoder == NULL) {
903 return errSecParam;
904 }
905 if(cmsEncoder->encState != ES_Init) {
906 return errSecParam;
907 }
908 cmsEncoder->detachedContent = detachedContent;
909 return errSecSuccess;
910 }
911
912 OSStatus CMSEncoderGetHasDetachedContent(
913 CMSEncoderRef cmsEncoder,
914 Boolean *detachedContent) /* RETURNED */
915 {
916 if((cmsEncoder == NULL) || (detachedContent == NULL)) {
917 return errSecParam;
918 }
919 *detachedContent = cmsEncoder->detachedContent;
920 return errSecSuccess;
921 }
922
923 /*
924 * Optionally specify an eContentType OID for the inner EncapsulatedData for
925 * a signed message. The default eContentType, used of this function is not
926 * called, is id-data.
927 */
928 OSStatus CMSEncoderSetEncapsulatedContentType(
929 CMSEncoderRef cmsEncoder,
930 const CSSM_OID *eContentType)
931 {
932 if((cmsEncoder == NULL) || (eContentType == NULL)) {
933 return errSecParam;
934 }
935 if(cmsEncoder->encState != ES_Init) {
936 return errSecParam;
937 }
938
939 CSSM_OID *ecOid = &cmsEncoder->eContentType;
940 if(ecOid->Data != NULL) {
941 free(ecOid->Data);
942 }
943 cmsCopyCmsData(eContentType, ecOid);
944 return errSecSuccess;
945 }
946
947 OSStatus CMSEncoderSetEncapsulatedContentTypeOID(
948 CMSEncoderRef cmsEncoder,
949 CFTypeRef eContentTypeOID)
950 {
951 // convert eContentTypeOID to a CSSM_OID
952 CSSM_OID contentType = { 0, NULL };
953 if (!eContentTypeOID || convertOid(eContentTypeOID, &contentType) != 0)
954 return errSecParam;
955 OSStatus result = CMSEncoderSetEncapsulatedContentType(cmsEncoder, &contentType);
956 if (contentType.Data)
957 free(contentType.Data);
958 return result;
959 }
960
961 /*
962 * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
963 */
964 OSStatus CMSEncoderCopyEncapsulatedContentType(
965 CMSEncoderRef cmsEncoder,
966 CFDataRef *eContentType)
967 {
968 if((cmsEncoder == NULL) || (eContentType == NULL)) {
969 return errSecParam;
970 }
971
972 CSSM_OID *ecOid = &cmsEncoder->eContentType;
973 if(ecOid->Data == NULL) {
974 *eContentType = NULL;
975 }
976 else {
977 *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
978 }
979 return errSecSuccess;
980 }
981
982 /*
983 * Optionally specify signed attributes. Only meaningful when creating a
984 * signed message. If this is called, it must be called before
985 * CMSEncoderUpdateContent().
986 */
987 OSStatus CMSEncoderAddSignedAttributes(
988 CMSEncoderRef cmsEncoder,
989 CMSSignedAttributes signedAttributes)
990 {
991 if(cmsEncoder == NULL) {
992 return errSecParam;
993 }
994 if(cmsEncoder->encState != ES_Init) {
995 return errSecParam;
996 }
997 cmsEncoder->signedAttributes |= signedAttributes;
998 return errSecSuccess;
999 }
1000
1001 /*
1002 * Set the signing time for a CMSEncoder.
1003 * This is only used if the kCMSAttrSigningTime attribute is included.
1004 */
1005 OSStatus CMSEncoderSetSigningTime(
1006 CMSEncoderRef cmsEncoder,
1007 CFAbsoluteTime time)
1008 {
1009 if(cmsEncoder == NULL) {
1010 return errSecParam;
1011 }
1012 if(cmsEncoder->encState != ES_Init) {
1013 return errSecParam;
1014 }
1015 cmsEncoder->signingTime = time;
1016 return errSecSuccess;
1017 }
1018
1019 /*
1020 * Set the hash agility attribute for a CMSEncoder.
1021 * This is only used if the kCMSAttrAppleCodesigningHashAgility attribute
1022 * is included.
1023 */
1024 OSStatus CMSEncoderSetAppleCodesigningHashAgility(
1025 CMSEncoderRef cmsEncoder,
1026 CFDataRef hashAgilityAttrValue)
1027 {
1028 if (cmsEncoder == NULL || cmsEncoder->encState != ES_Init) {
1029 return errSecParam;
1030 }
1031 cmsEncoder->hashAgilityAttrValue = CFRetainSafe(hashAgilityAttrValue);
1032 return errSecSuccess;
1033 }
1034
1035 OSStatus CMSEncoderSetCertificateChainMode(
1036 CMSEncoderRef cmsEncoder,
1037 CMSCertificateChainMode chainMode)
1038 {
1039 if(cmsEncoder == NULL) {
1040 return errSecParam;
1041 }
1042 if(cmsEncoder->encState != ES_Init) {
1043 return errSecParam;
1044 }
1045 switch(chainMode) {
1046 case kCMSCertificateNone:
1047 case kCMSCertificateSignerOnly:
1048 case kCMSCertificateChain:
1049 case kCMSCertificateChainWithRoot:
1050 break;
1051 default:
1052 return errSecParam;
1053 }
1054 cmsEncoder->chainMode = chainMode;
1055 return errSecSuccess;
1056 }
1057
1058 OSStatus CMSEncoderGetCertificateChainMode(
1059 CMSEncoderRef cmsEncoder,
1060 CMSCertificateChainMode *chainModeOut)
1061 {
1062 if(cmsEncoder == NULL) {
1063 return errSecParam;
1064 }
1065 *chainModeOut = cmsEncoder->chainMode;
1066 return errSecSuccess;
1067 }
1068
1069 void
1070 CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback)
1071 {
1072 if (cmsEncoder->cmsMsg)
1073 SecCmsMessageSetTSACallback(cmsEncoder->cmsMsg, tsaCallback);
1074 }
1075
1076 void
1077 CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder, CFTypeRef tsaContext)
1078 {
1079 if (cmsEncoder->cmsMsg)
1080 SecCmsMessageSetTSAContext(cmsEncoder->cmsMsg, tsaContext);
1081 }
1082
1083 #pragma mark --- Action ---
1084
1085 /*
1086 * Feed content bytes into the encoder.
1087 * Can be called multiple times.
1088 * No 'setter' routines can be called after this function has been called.
1089 */
1090 OSStatus CMSEncoderUpdateContent(
1091 CMSEncoderRef cmsEncoder,
1092 const void *content,
1093 size_t contentLen)
1094 {
1095 if(cmsEncoder == NULL) {
1096 return errSecParam;
1097 }
1098
1099 OSStatus ortn = errSecSuccess;
1100 switch(cmsEncoder->encState) {
1101 case ES_Init:
1102 /*
1103 * First time thru: do the CmsMsg setup.
1104 */
1105 ortn = cmsSetupCmsMsg(cmsEncoder);
1106 if(ortn) {
1107 return ortn;
1108 }
1109 /* fall thru to set up the encoder */
1110
1111 case ES_Msg:
1112 /* We have a cmsMsg but no encoder; create one */
1113 ASSERT(cmsEncoder->cmsMsg != NULL);
1114 ASSERT(cmsEncoder->encoder == NULL);
1115 ortn = cmsSetupEncoder(cmsEncoder);
1116 if(ortn) {
1117 return ortn;
1118 }
1119 /* only legal calls now are update and finalize */
1120 cmsEncoder->encState = ES_Updating;
1121 break;
1122
1123 case ES_Updating:
1124 ASSERT(cmsEncoder->encoder != NULL);
1125 break;
1126
1127 case ES_Final:
1128 /* Too late for another update */
1129 return errSecParam;
1130
1131 default:
1132 return errSecInternalComponent;
1133 }
1134
1135 /* FIXME - CFIndex same size as size_t on 64bit? */
1136 ortn = SecCmsEncoderUpdate(cmsEncoder->encoder, content, (CFIndex)contentLen);
1137 if(ortn) {
1138 ortn = cmsRtnToOSStatus(ortn);
1139 CSSM_PERROR("SecCmsEncoderUpdate", ortn);
1140 }
1141 return ortn;
1142 }
1143
1144 /*
1145 * Finish encoding the message and obtain the encoded result.
1146 * Caller must CFRelease the result.
1147 */
1148 OSStatus CMSEncoderCopyEncodedContent(
1149 CMSEncoderRef cmsEncoder,
1150 CFDataRef *encodedContent)
1151 {
1152 if((cmsEncoder == NULL) || (encodedContent == NULL)) {
1153 return errSecParam;
1154 }
1155
1156 OSStatus ortn;
1157
1158 switch(cmsEncoder->encState) {
1159 case ES_Updating:
1160 /* normal termination */
1161 break;
1162 case ES_Final:
1163 /* already been called */
1164 return errSecParam;
1165 case ES_Msg:
1166 case ES_Init:
1167 /*
1168 * The only time these are legal is when we're doing a SignedData
1169 * with certificates only (no signers, no content).
1170 */
1171 if((cmsEncoder->signers != NULL) ||
1172 (cmsEncoder->recipients != NULL) ||
1173 (cmsEncoder->otherCerts == NULL)) {
1174 return errSecParam;
1175 }
1176
1177 /* Set up for certs only */
1178 ortn = cmsSetupForSignedData(cmsEncoder);
1179 if(ortn) {
1180 return ortn;
1181 }
1182 /* and an encoder */
1183 ortn = cmsSetupEncoder(cmsEncoder);
1184 if(ortn) {
1185 return ortn;
1186 }
1187 break;
1188 }
1189
1190
1191 ASSERT(cmsEncoder->encoder != NULL);
1192 ortn = SecCmsEncoderFinish(cmsEncoder->encoder);
1193 /* regardless of the outcome, the encoder itself has been freed */
1194 cmsEncoder->encoder = NULL;
1195 if(ortn) {
1196 return cmsRtnToOSStatus(ortn);
1197 }
1198 cmsEncoder->encState = ES_Final;
1199
1200 if((cmsEncoder->encoderOut.Data == NULL) && !cmsEncoder->customCoder) {
1201 /* not sure how this could happen... */
1202 dprintf("Successful encode, but no data\n");
1203 return errSecInternalComponent;
1204 }
1205 if(cmsEncoder->customCoder) {
1206 /* we're done */
1207 *encodedContent = NULL;
1208 return errSecSuccess;
1209 }
1210
1211 /* in two out of three cases, we're done */
1212 switch(cmsEncoder->op) {
1213 case EO_Sign:
1214 case EO_Encrypt:
1215 *encodedContent = CFDataCreate(NULL, (const UInt8 *)cmsEncoder->encoderOut.Data,
1216 cmsEncoder->encoderOut.Length);
1217 return errSecSuccess;
1218 case EO_SignEncrypt:
1219 /* proceed, more work to do */
1220 break;
1221 }
1222
1223 /*
1224 * Signing & encrypting.
1225 * Due to bugs in the libsecurity_smime encoder, it can't encode nested
1226 * ContentInfos in one shot. So we do another pass, specifying the SignedData
1227 * inside of the ContentInfo we just created as the data to encrypt.
1228 */
1229 SecAsn1CoderRef asn1Coder = NULL;
1230 CSSM_DATA signedData = {0, NULL};
1231
1232 ortn = SecAsn1CoderCreate(&asn1Coder);
1233 if(ortn) {
1234 return ortn;
1235 }
1236 ortn = cmsContentInfoContent(asn1Coder, &cmsEncoder->encoderOut, &signedData);
1237 if(ortn) {
1238 goto errOut;
1239 }
1240
1241 /* now just encrypt that, one-shot */
1242 ortn = CMSEncode(NULL, /* no signers this time */
1243 cmsEncoder->recipients,
1244 &CSSMOID_PKCS7_SignedData, /* fake out encoder so it doesn't try to actually
1245 * encode the signedData - this asserts the
1246 * SEC_OID_OTHER OID tag in the EnvelopedData's
1247 * ContentInfo */
1248 FALSE, /* detachedContent */
1249 kCMSAttrNone, /* signedAttributes - none this time */
1250 signedData.Data, signedData.Length,
1251 encodedContent);
1252
1253 errOut:
1254 if(asn1Coder) {
1255 SecAsn1CoderRelease(asn1Coder);
1256 }
1257 return ortn;
1258 }
1259
1260 #pragma mark --- High-level API ---
1261
1262 /*
1263 * High-level, one-shot encoder function.
1264 */
1265 OSStatus CMSEncode(
1266 CFTypeRef signers,
1267 CFTypeRef recipients,
1268 const CSSM_OID *eContentType,
1269 Boolean detachedContent,
1270 CMSSignedAttributes signedAttributes,
1271 const void *content,
1272 size_t contentLen,
1273 CFDataRef *encodedContent) /* RETURNED */
1274 {
1275 if((signers == NULL) && (recipients == NULL)) {
1276 return errSecParam;
1277 }
1278 if(encodedContent == NULL) {
1279 return errSecParam;
1280 }
1281
1282 CMSEncoderRef cmsEncoder;
1283 OSStatus ortn;
1284
1285 /* set up the encoder */
1286 ortn = CMSEncoderCreate(&cmsEncoder);
1287 if(ortn) {
1288 return ortn;
1289 }
1290
1291 /* subsequent errors to errOut: */
1292 if(signers) {
1293 ortn = CMSEncoderAddSigners(cmsEncoder, signers);
1294 if(ortn) {
1295 goto errOut;
1296 }
1297 }
1298 if(recipients) {
1299 ortn = CMSEncoderAddRecipients(cmsEncoder, recipients);
1300 if(ortn) {
1301 goto errOut;
1302 }
1303 }
1304 if(eContentType) {
1305 ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
1306 if(ortn) {
1307 goto errOut;
1308 }
1309 }
1310 if(detachedContent) {
1311 ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
1312 if(ortn) {
1313 goto errOut;
1314 }
1315 }
1316 if(signedAttributes) {
1317 ortn = CMSEncoderAddSignedAttributes(cmsEncoder, signedAttributes);
1318 if(ortn) {
1319 goto errOut;
1320 }
1321 }
1322 /* GO */
1323 ortn = CMSEncoderUpdateContent(cmsEncoder, content, contentLen);
1324 if(ortn) {
1325 goto errOut;
1326 }
1327 ortn = CMSEncoderCopyEncodedContent(cmsEncoder, encodedContent);
1328
1329 errOut:
1330 CFRelease(cmsEncoder);
1331 return ortn;
1332 }
1333
1334 OSStatus CMSEncodeContent(
1335 CFTypeRef signers,
1336 CFTypeRef recipients,
1337 CFTypeRef eContentTypeOID,
1338 Boolean detachedContent,
1339 CMSSignedAttributes signedAttributes,
1340 const void *content,
1341 size_t contentLen,
1342 CFDataRef *encodedContentOut) /* RETURNED */
1343 {
1344 // convert eContentTypeOID to a CSSM_OID
1345 CSSM_OID contentType = { 0, NULL };
1346 if (eContentTypeOID && convertOid(eContentTypeOID, &contentType) != 0)
1347 return errSecParam;
1348 const CSSM_OID *contentTypePtr = (eContentTypeOID) ? &contentType : NULL;
1349 OSStatus result = CMSEncode(signers, recipients, contentTypePtr,
1350 detachedContent, signedAttributes,
1351 content, contentLen, encodedContentOut);
1352 if (contentType.Data)
1353 free(contentType.Data);
1354 return result;
1355 }
1356
1357 #pragma mark --- SPI routines declared in CMSPrivate.h ---
1358
1359 /*
1360 * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
1361 * If we don't have a SecCmsMessageRef yet, we create one now.
1362 * This is the only place where we go to state ES_Msg.
1363 */
1364 OSStatus CMSEncoderGetCmsMessage(
1365 CMSEncoderRef cmsEncoder,
1366 SecCmsMessageRef *cmsMessage) /* RETURNED */
1367 {
1368 if((cmsEncoder == NULL) || (cmsMessage == NULL)) {
1369 return errSecParam;
1370 }
1371 if(cmsEncoder->cmsMsg != NULL) {
1372 ASSERT(cmsEncoder->encState != ES_Init);
1373 *cmsMessage = cmsEncoder->cmsMsg;
1374 return errSecSuccess;
1375 }
1376
1377 OSStatus ortn = cmsSetupCmsMsg(cmsEncoder);
1378 if(ortn) {
1379 return ortn;
1380 }
1381 *cmsMessage = cmsEncoder->cmsMsg;
1382
1383 /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
1384 cmsEncoder->encState = ES_Msg;
1385 return errSecSuccess;
1386 }
1387
1388 /*
1389 * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
1390 * If this is called, it must be called before the first call to
1391 * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
1392 * incoming SecCmsEncoderRef.
1393 */
1394 OSStatus CMSEncoderSetEncoder(
1395 CMSEncoderRef cmsEncoder,
1396 SecCmsEncoderRef encoder)
1397 {
1398 if((cmsEncoder == NULL) || (encoder == NULL)) {
1399 return errSecParam;
1400 }
1401
1402 OSStatus ortn;
1403
1404 switch(cmsEncoder->encState) {
1405 case ES_Init:
1406 /* No message, no encoder */
1407 ASSERT(cmsEncoder->cmsMsg == NULL);
1408 ASSERT(cmsEncoder->encoder == NULL);
1409 ortn = cmsSetupCmsMsg(cmsEncoder);
1410 if(ortn) {
1411 return ortn;
1412 }
1413 /* drop thru to set encoder */
1414 case ES_Msg:
1415 /* cmsMsg but no encoder */
1416 ASSERT(cmsEncoder->cmsMsg != NULL);
1417 ASSERT(cmsEncoder->encoder == NULL);
1418 cmsEncoder->encoder = encoder;
1419 cmsEncoder->encState = ES_Updating;
1420 cmsEncoder->customCoder = true; /* we won't see data */
1421 return errSecSuccess;
1422 default:
1423 /* no can do, too late */
1424 return errSecParam;
1425 }
1426 }
1427
1428 /*
1429 * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
1430 * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
1431 * CMSEncoderUpdateContent() has been called.
1432 * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
1433 */
1434 OSStatus CMSEncoderGetEncoder(
1435 CMSEncoderRef cmsEncoder,
1436 SecCmsEncoderRef *encoder) /* RETURNED */
1437 {
1438 if((cmsEncoder == NULL) || (encoder == NULL)) {
1439 return errSecParam;
1440 }
1441
1442 /* any state, whether we have an encoder or not is OK */
1443 *encoder = cmsEncoder->encoder;
1444 return errSecSuccess;
1445 }
1446
1447 #include <AssertMacros.h>
1448
1449 /*
1450 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
1451 * present. This timestamp is an authenticated timestamp provided by
1452 * a timestamping authority.
1453 *
1454 * Returns errSecParam if the CMS message was not signed or if signerIndex
1455 * is greater than the number of signers of the message minus one.
1456 *
1457 * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
1458 */
1459 OSStatus CMSEncoderCopySignerTimestamp(
1460 CMSEncoderRef cmsEncoder,
1461 size_t signerIndex, /* usually 0 */
1462 CFAbsoluteTime *timestamp) /* RETURNED */
1463 {
1464 return CMSEncoderCopySignerTimestampWithPolicy(
1465 cmsEncoder,
1466 NULL,
1467 signerIndex,
1468 timestamp);
1469 }
1470
1471 OSStatus CMSEncoderCopySignerTimestampWithPolicy(
1472 CMSEncoderRef cmsEncoder,
1473 CFTypeRef timeStampPolicy,
1474 size_t signerIndex, /* usually 0 */
1475 CFAbsoluteTime *timestamp) /* RETURNED */
1476 {
1477 OSStatus status = errSecParam;
1478 SecCmsMessageRef cmsg;
1479 SecCmsSignedDataRef signedData = NULL;
1480 int numContentInfos = 0;
1481
1482 require(cmsEncoder && timestamp, xit);
1483 require_noerr(CMSEncoderGetCmsMessage(cmsEncoder, &cmsg), xit);
1484 numContentInfos = SecCmsMessageContentLevelCount(cmsg);
1485 for (int dex = 0; !signedData && dex < numContentInfos; dex++)
1486 {
1487 SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
1488 SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
1489 if (tag == SEC_OID_PKCS7_SIGNED_DATA)
1490 if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
1491 if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
1492 {
1493 status = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp);
1494 break;
1495 }
1496 }
1497
1498 xit:
1499 return status;
1500 }