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