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