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