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