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