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