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