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