--- /dev/null
+/*
+ * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * CMSEncoder.cpp - encode, sign, and/or encrypt CMS messages.
+ */
+
+#include "CMSEncoder.h"
+#include "CMSPrivate.h"
+#include "CMSUtils.h"
+#include <Security/SecBase.h>
+#include <Security/SecCmsEncoder.h>
+#include <Security/SecCmsEnvelopedData.h>
+#include <Security/SecCmsMessage.h>
+#include <Security/SecCmsRecipientInfo.h>
+#include <Security/SecCmsSignedData.h>
+#include <Security/SecCmsSignerInfo.h>
+#include <Security/SecCmsContentInfo.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecKeychain.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecSMIME.h>
+#include <Security/oidsattr.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/SecAsn1Types.h>
+#include <Security/SecAsn1Templates.h>
+#include <CoreFoundation/CFRuntime.h>
+#include <pthread.h>
+
+#include <security_smime/tsaSupport.h>
+#include <security_smime/cmspriv.h>
+
+#pragma mark --- Private types and definitions ---
+
+/*
+ * Encoder state.
+ */
+typedef enum {
+ ES_Init, /* between CMSEncoderCreate and earlier of CMSEncoderUpdateContent
+ * and CMSEncodeGetCmsMessage */
+ ES_Msg, /* created cmsMsg in CMSEncodeGetCmsMessage, but no encoder yet */
+ ES_Updating, /* between first CMSEncoderUpdateContent and CMSEncoderCopyEncodedContent */
+ ES_Final /* CMSEncoderCopyEncodedContent has been called */
+} CMSEncoderState;
+
+/*
+ * High-level operation: what are we doing?
+ */
+typedef enum {
+ EO_Sign,
+ EO_Encrypt,
+ EO_SignEncrypt
+} CMSEncoderOp;
+
+/*
+ * Caller's CMSEncoderRef points to one of these.
+ */
+struct _CMSEncoder {
+ CFRuntimeBase base;
+ CMSEncoderState encState;
+ CMSEncoderOp op;
+ Boolean detachedContent;
+ CSSM_OID eContentType;
+ CFMutableArrayRef signers;
+ CFMutableArrayRef recipients;
+ CFMutableArrayRef otherCerts;
+ CMSSignedAttributes signedAttributes;
+ CFAbsoluteTime signingTime;
+ SecCmsMessageRef cmsMsg;
+ SecArenaPoolRef arena; /* the encoder's arena */
+ SecCmsEncoderRef encoder;
+ CSSM_DATA encoderOut; /* output goes here... */
+ bool customCoder; /* unless this is set by
+ * CMSEncoderSetEncoder */
+ CMSCertificateChainMode chainMode;
+};
+
+static void cmsEncoderInit(CFTypeRef enc);
+static void cmsEncoderFinalize(CFTypeRef enc);
+
+static CFRuntimeClass cmsEncoderRuntimeClass =
+{
+ 0, /* version */
+ "CMSEncoder",
+ cmsEncoderInit,
+ NULL, /* copy */
+ cmsEncoderFinalize,
+ NULL, /* equal - just use pointer equality */
+ NULL, /* hash, ditto */
+ NULL, /* copyFormattingDesc */
+ NULL /* copyDebugDesc */
+};
+
+void
+CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback);
+
+#pragma mark --- Private routines ---
+
+/*
+ * Decode a CFStringRef representation of an integer
+ */
+static int cfStringToNumber(
+ CFStringRef inStr)
+{
+ int max = 32;
+ char buf[max];
+ if (!inStr || !CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
+ return -1;
+ return atoi(buf);
+}
+
+/*
+ * Encode an integer component of an OID, return resulting number of bytes;
+ * actual bytes are mallocd and returned in *encodeArray.
+ */
+static unsigned encodeNumber(
+ int num,
+ unsigned char **encodeArray) // mallocd and RETURNED
+{
+ unsigned char *result;
+ unsigned dex;
+ unsigned numDigits = 0;
+ unsigned scratch;
+
+ /* trival case - 0 maps to 0 */
+ if(num == 0) {
+ *encodeArray = (unsigned char *)malloc(1);
+ **encodeArray = 0;
+ return 1;
+ }
+
+ /* first calculate the number of digits in num, base 128 */
+ scratch = (unsigned)num;
+ while(scratch != 0) {
+ numDigits++;
+ scratch >>= 7;
+ }
+
+ result = (unsigned char *)malloc(numDigits);
+ scratch = (unsigned)num;
+ for(dex=0; dex<numDigits; dex++) {
+ result[numDigits - dex - 1] = scratch & 0x7f;
+ scratch >>= 7;
+ }
+
+ /* all digits except the last one have m.s. bit set */
+ for(dex=0; dex<(numDigits - 1); dex++) {
+ result[dex] |= 0x80;
+ }
+
+ *encodeArray = result;
+ return numDigits;
+}
+
+/*
+ * Given an OID in dotted-decimal string representation, convert to binary
+ * DER format. Returns a pointer in outOid which the caller must free(),
+ * as well as the length of the data in outLen.
+ * Function returns 0 if successful, non-zero otherwise.
+ */
+static int encodeOid(
+ const unsigned char *inStr,
+ unsigned char **outOid,
+ unsigned int *outLen)
+{
+ unsigned char **digits = NULL; /* array of char * from encodeNumber */
+ unsigned *numDigits = NULL; /* array of unsigned from encodeNumber */
+ CFIndex digit;
+ unsigned numDigitBytes; /* total #of output chars */
+ unsigned char firstByte;
+ unsigned char *outP;
+ CFIndex numsToProcess;
+ CFStringRef oidStr = NULL;
+ CFArrayRef argvRef = NULL;
+ int num, result = 1;
+ CFIndex argc;
+
+ /* parse input string into array of substrings */
+ if (!inStr || !outOid || !outLen) goto cleanExit;
+ oidStr = CFStringCreateWithCString(NULL, (const char *)inStr, kCFStringEncodingASCII);
+ if (!oidStr) goto cleanExit;
+ argvRef = CFStringCreateArrayBySeparatingStrings(NULL, oidStr, CFSTR("."));
+ if (!argvRef) goto cleanExit;
+ argc = CFArrayGetCount(argvRef);
+ if (argc < 3) goto cleanExit;
+
+ /* first two numbers in OID munge together */
+ num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 0));
+ if (num < 0) goto cleanExit;
+ firstByte = (40 * num);
+ num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 1));
+ if (num < 0) goto cleanExit;
+ firstByte += num;
+ numDigitBytes = 1;
+
+ numsToProcess = argc - 2;
+ if(numsToProcess > 0) {
+ /* skip this loop in the unlikely event that input is only two numbers */
+ digits = (unsigned char **) malloc(numsToProcess * sizeof(unsigned char *));
+ numDigits = (unsigned *) malloc(numsToProcess * sizeof(unsigned));
+ for(digit=0; digit<numsToProcess; digit++) {
+ num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, digit+2));
+ if (num < 0) goto cleanExit;
+ numDigits[digit] = encodeNumber(num, &digits[digit]);
+ numDigitBytes += numDigits[digit];
+ }
+ }
+ *outLen = (2 + numDigitBytes);
+ *outOid = outP = (unsigned char *) malloc(*outLen);
+ *outP++ = 0x06;
+ *outP++ = numDigitBytes;
+ *outP++ = firstByte;
+ for(digit=0; digit<numsToProcess; digit++) {
+ unsigned int byteDex;
+ for(byteDex=0; byteDex<numDigits[digit]; byteDex++) {
+ *outP++ = digits[digit][byteDex];
+ }
+ }
+ if(digits) {
+ for(digit=0; digit<numsToProcess; digit++) {
+ free(digits[digit]);
+ }
+ free(digits);
+ free(numDigits);
+ }
+ result = 0;
+
+cleanExit:
+ if (oidStr) CFRelease(oidStr);
+ if (argvRef) CFRelease(argvRef);
+
+ return result;
+}
+
+/*
+ * Given a CF object reference describing an OID, convert to binary DER format
+ * and fill out the CSSM_OID structure provided by the caller. Caller is
+ * responsible for freeing the data pointer in outOid->Data.
+ *
+ * Function returns 0 if successful, non-zero otherwise.
+ */
+
+static int convertOid(
+ CFTypeRef inRef,
+ CSSM_OID *outOid)
+{
+ if (!inRef || !outOid)
+ return errSecParam;
+
+ unsigned char *oidData = NULL;
+ unsigned int oidLen = 0;
+
+ if (CFGetTypeID(inRef) == CFStringGetTypeID()) {
+ // CFStringRef: OID representation is a dotted-decimal string
+ CFStringRef inStr = (CFStringRef)inRef;
+ CFIndex max = CFStringGetLength(inStr) * 3;
+ char buf[max];
+ if (!CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
+ return errSecParam;
+
+ if(encodeOid((unsigned char *)buf, &oidData, &oidLen) != 0)
+ return errSecParam;
+ }
+ else if (CFGetTypeID(inRef) == CFDataGetTypeID()) {
+ // CFDataRef: OID representation is in binary DER format
+ CFDataRef inData = (CFDataRef)inRef;
+ oidLen = (unsigned int) CFDataGetLength(inData);
+ oidData = (unsigned char *) malloc(oidLen);
+ memcpy(oidData, CFDataGetBytePtr(inData), oidLen);
+ }
+ else {
+ // Not in a format we understand
+ return errSecParam;
+ }
+ outOid->Length = oidLen;
+ outOid->Data = (uint8 *)oidData;
+ return 0;
+}
+
+static CFTypeID cmsEncoderTypeID = _kCFRuntimeNotATypeID;
+
+/* one time only class init, called via pthread_once() in CMSEncoderGetTypeID() */
+static void cmsEncoderClassInitialize(void)
+{
+ cmsEncoderTypeID =
+ _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsEncoderRuntimeClass);
+}
+
+/* init called out from _CFRuntimeCreateInstance() */
+static void cmsEncoderInit(CFTypeRef enc)
+{
+ char *start = ((char *)enc) + sizeof(CFRuntimeBase);
+ memset(start, 0, sizeof(struct _CMSEncoder) - sizeof(CFRuntimeBase));
+}
+
+/*
+ * Dispose of a CMSEncoder. Called out from CFRelease().
+ */
+static void cmsEncoderFinalize(
+ CFTypeRef enc)
+{
+ CMSEncoderRef cmsEncoder = (CMSEncoderRef)enc;
+ if(cmsEncoder == NULL) {
+ return;
+ }
+ if(cmsEncoder->eContentType.Data != NULL) {
+ free(cmsEncoder->eContentType.Data);
+ }
+ CFRELEASE(cmsEncoder->signers);
+ CFRELEASE(cmsEncoder->recipients);
+ CFRELEASE(cmsEncoder->otherCerts);
+ if(cmsEncoder->cmsMsg != NULL) {
+ SecCmsMessageDestroy(cmsEncoder->cmsMsg);
+ }
+ if(cmsEncoder->arena != NULL) {
+ SecArenaPoolFree(cmsEncoder->arena, false);
+ }
+ if(cmsEncoder->encoder != NULL) {
+ /*
+ * Normally this gets freed in SecCmsEncoderFinish - this is
+ * an error case.
+ */
+ SecCmsEncoderDestroy(cmsEncoder->encoder);
+ }
+}
+
+static OSStatus cmsSetupEncoder(
+ CMSEncoderRef cmsEncoder)
+{
+ OSStatus ortn;
+
+ ASSERT(cmsEncoder->arena == NULL);
+ ASSERT(cmsEncoder->encoder == NULL);
+
+ ortn = SecArenaPoolCreate(1024, &cmsEncoder->arena);
+ if(ortn) {
+ return cmsRtnToOSStatus(ortn);
+ }
+ ortn = SecCmsEncoderCreate(cmsEncoder->cmsMsg,
+ NULL, NULL, // no callback
+ &cmsEncoder->encoderOut, // data goes here
+ cmsEncoder->arena,
+ NULL, NULL, // no password callback (right?)
+ NULL, NULL, // decrypt key callback
+ NULL, NULL, // detached digests
+ &cmsEncoder->encoder);
+ if(ortn) {
+ return cmsRtnToOSStatus(ortn);
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Set up a SecCmsMessageRef for a SignedData creation.
+ */
+static OSStatus cmsSetupForSignedData(
+ CMSEncoderRef cmsEncoder)
+{
+ ASSERT((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL));
+
+ SecCmsContentInfoRef contentInfo = NULL;
+ SecCmsSignedDataRef signedData = NULL;
+ OSStatus ortn;
+
+ /* build chain of objects: message->signedData->data */
+ if(cmsEncoder->cmsMsg != NULL) {
+ SecCmsMessageDestroy(cmsEncoder->cmsMsg);
+ }
+ cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
+ if(cmsEncoder->cmsMsg == NULL) {
+ return errSecInternalComponent;
+ }
+
+ signedData = SecCmsSignedDataCreate(cmsEncoder->cmsMsg);
+ if(signedData == NULL) {
+ return errSecInternalComponent;
+ }
+ contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
+ ortn = SecCmsContentInfoSetContentSignedData(cmsEncoder->cmsMsg, contentInfo,
+ signedData);
+ if(ortn) {
+ return cmsRtnToOSStatus(ortn);
+ }
+ contentInfo = SecCmsSignedDataGetContentInfo(signedData);
+ if(cmsEncoder->eContentType.Data != NULL) {
+ /* Override the default eContentType of id-data */
+ ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
+ contentInfo,
+ NULL, /* data - provided to encoder, not here */
+ cmsEncoder->detachedContent,
+ &cmsEncoder->eContentType);
+ }
+ else {
+ ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
+ contentInfo,
+ NULL, /* data - provided to encoder, not here */
+ cmsEncoder->detachedContent);
+ }
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsContentInfoSetContent*", ortn);
+ return ortn;
+ }
+
+ /* optional 'global' (per-SignedData) certs */
+ if(cmsEncoder->otherCerts != NULL) {
+ ortn = SecCmsSignedDataAddCertList(signedData, cmsEncoder->otherCerts);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignedDataAddCertList", ortn);
+ return ortn;
+ }
+ }
+
+ /* SignerInfos, one per signer */
+ CFIndex numSigners = 0;
+ if(cmsEncoder->signers != NULL) {
+ /* this is optional...in case we're just creating a cert bundle */
+ numSigners = CFArrayGetCount(cmsEncoder->signers);
+ }
+ CFIndex dex;
+ SecKeychainRef ourKc = NULL;
+ SecCertificateRef ourCert = NULL;
+ SecCmsCertChainMode chainMode = SecCmsCMCertChain;
+
+ switch(cmsEncoder->chainMode) {
+ case kCMSCertificateNone:
+ chainMode = SecCmsCMNone;
+ break;
+ case kCMSCertificateSignerOnly:
+ chainMode = SecCmsCMCertOnly;
+ break;
+ case kCMSCertificateChainWithRoot:
+ chainMode = SecCmsCMCertChainWithRoot;
+ break;
+ default:
+ break;
+ }
+ for(dex=0; dex<numSigners; dex++) {
+ SecCmsSignerInfoRef signerInfo;
+
+ SecIdentityRef ourId =
+ (SecIdentityRef)CFArrayGetValueAtIndex(cmsEncoder->signers, dex);
+ ortn = SecIdentityCopyCertificate(ourId, &ourCert);
+ if(ortn) {
+ CSSM_PERROR("SecIdentityCopyCertificate", ortn);
+ break;
+ }
+ ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
+ if(ortn) {
+ CSSM_PERROR("SecKeychainItemCopyKeychain", ortn);
+ break;
+ }
+ signerInfo = SecCmsSignerInfoCreate(cmsEncoder->cmsMsg, ourId, SEC_OID_SHA1);
+ if (signerInfo == NULL) {
+ ortn = errSecInternalComponent;
+ break;
+ }
+
+ /* we want the cert chain included for this one */
+ /* NOTE the usage parameter is currently unused by the SMIME lib */
+ ortn = SecCmsSignerInfoIncludeCerts(signerInfo, chainMode,
+ certUsageEmailSigner);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn);
+ break;
+ }
+
+ /* other options */
+ if(cmsEncoder->signedAttributes & kCMSAttrSmimeCapabilities) {
+ ortn = SecCmsSignerInfoAddSMIMECaps(signerInfo);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
+ break;
+ }
+ }
+ if(cmsEncoder->signedAttributes & kCMSAttrSmimeEncryptionKeyPrefs) {
+ ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
+ break;
+ }
+ }
+ if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) {
+ ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn);
+ break;
+ }
+ }
+ if(cmsEncoder->signedAttributes & kCMSAttrSigningTime) {
+ if (cmsEncoder->signingTime == 0)
+ cmsEncoder->signingTime = CFAbsoluteTimeGetCurrent();
+ ortn = SecCmsSignerInfoAddSigningTime(signerInfo, cmsEncoder->signingTime);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn);
+ break;
+ }
+ }
+
+ ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn);
+ break;
+ }
+
+ CFRELEASE(ourKc);
+ CFRELEASE(ourCert);
+ ourKc = NULL;
+ ourCert = NULL;
+ }
+ if(ortn) {
+ CFRELEASE(ourKc);
+ CFRELEASE(ourCert);
+ }
+ return ortn;
+}
+
+/*
+ * Set up a SecCmsMessageRef for a EnvelopedData creation.
+ */
+static OSStatus cmsSetupForEnvelopedData(
+ CMSEncoderRef cmsEncoder)
+{
+ ASSERT(cmsEncoder->op == EO_Encrypt);
+ ASSERT(cmsEncoder->recipients != NULL);
+
+ SecCmsContentInfoRef contentInfo = NULL;
+ SecCmsEnvelopedDataRef envelopedData = NULL;
+ SECOidTag algorithmTag;
+ int keySize;
+ OSStatus ortn;
+
+ /*
+ * Find encryption algorithm...unfortunately we need a NULL-terminated array
+ * of SecCertificateRefs for this.
+ */
+ CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients);
+ CFIndex dex;
+ SecCertificateRef *certArray = (SecCertificateRef *)malloc(
+ (numCerts+1) * sizeof(SecCertificateRef));
+
+ for(dex=0; dex<numCerts; dex++) {
+ certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex(
+ cmsEncoder->recipients, dex);
+ }
+ certArray[numCerts] = NULL;
+ ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize);
+ free(certArray);
+ if(ortn) {
+ CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn);
+ return ortn;
+ }
+
+ /* build chain of objects: message->envelopedData->data */
+ if(cmsEncoder->cmsMsg != NULL) {
+ SecCmsMessageDestroy(cmsEncoder->cmsMsg);
+ }
+ cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
+ if(cmsEncoder->cmsMsg == NULL) {
+ return errSecInternalComponent;
+ }
+ envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg,
+ algorithmTag, keySize);
+ if(envelopedData == NULL) {
+ return errSecInternalComponent;
+ }
+ contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
+ ortn = SecCmsContentInfoSetContentEnvelopedData(cmsEncoder->cmsMsg,
+ contentInfo, envelopedData);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn);
+ return ortn;
+ }
+ contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
+ if(cmsEncoder->eContentType.Data != NULL) {
+ /* Override the default ContentType of id-data */
+ ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
+ contentInfo,
+ NULL, /* data - provided to encoder, not here */
+ FALSE, /* detachedContent */
+ &cmsEncoder->eContentType);
+ }
+ else {
+ ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
+ contentInfo,
+ NULL /* data - provided to encoder, not here */,
+ cmsEncoder->detachedContent);
+ }
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn);
+ return ortn;
+ }
+
+ /*
+ * create & attach recipient information, one for each recipient
+ */
+ for(dex=0; dex<numCerts; dex++) {
+ SecCmsRecipientInfoRef recipientInfo = NULL;
+
+ SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex(
+ cmsEncoder->recipients, dex);
+ recipientInfo = SecCmsRecipientInfoCreate(cmsEncoder->cmsMsg, thisRecip);
+ ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn);
+ return ortn;
+ }
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Set up cmsMsg. Called from either the first call to CMSEncoderUpdateContent, or
+ * from CMSEncodeGetCmsMessage().
+ */
+static OSStatus cmsSetupCmsMsg(
+ CMSEncoderRef cmsEncoder)
+{
+ ASSERT(cmsEncoder != NULL);
+ ASSERT(cmsEncoder->encState == ES_Init);
+
+ /* figure out what high-level operation we're doing */
+ if((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL)) {
+ if(cmsEncoder->recipients != NULL) {
+ cmsEncoder->op = EO_SignEncrypt;
+ }
+ else {
+ cmsEncoder->op = EO_Sign;
+ }
+ }
+ else if(cmsEncoder->recipients != NULL) {
+ cmsEncoder->op = EO_Encrypt;
+ }
+ else {
+ dprintf("CMSEncoderUpdateContent: nothing to do\n");
+ return errSecParam;
+ }
+
+ OSStatus ortn = errSecSuccess;
+
+ switch(cmsEncoder->op) {
+ case EO_Sign:
+ case EO_SignEncrypt:
+ /* If we're signing & encrypting, do the signing first */
+ ortn = cmsSetupForSignedData(cmsEncoder);
+ break;
+ case EO_Encrypt:
+ ortn = cmsSetupForEnvelopedData(cmsEncoder);
+ break;
+ }
+ cmsEncoder->encState = ES_Msg;
+ return ortn;
+}
+
+/*
+ * ASN.1 template for decoding a ContentInfo.
+ */
+typedef struct {
+ CSSM_OID contentType;
+ CSSM_DATA content;
+} SimpleContentInfo;
+
+static const SecAsn1Template cmsSimpleContentInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SimpleContentInfo, content),
+ kSecAsn1AnyTemplate },
+ { 0, }
+};
+
+/*
+ * Obtain the content of a contentInfo, This basically strips off the contentType OID
+ * and returns its ASN_ANY content, allocated the provided coder's memory space.
+ */
+static OSStatus cmsContentInfoContent(
+ SecAsn1CoderRef asn1Coder,
+ const CSSM_DATA *contentInfo,
+ CSSM_DATA *content) /* RETURNED */
+{
+ OSStatus ortn;
+ SimpleContentInfo decodedInfo;
+
+ memset(&decodedInfo, 0, sizeof(decodedInfo));
+ ortn = SecAsn1DecodeData(asn1Coder, contentInfo,
+ cmsSimpleContentInfoTemplate, &decodedInfo);
+ if(ortn) {
+ return ortn;
+ }
+ if(decodedInfo.content.Data == NULL) {
+ dprintf("***Error decoding contentInfo: no content\n");
+ return errSecInternalComponent;
+ }
+ *content = decodedInfo.content;
+ return errSecSuccess;
+}
+
+#pragma mark --- Start of Public API ---
+
+CFTypeID CMSEncoderGetTypeID(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+ if(cmsEncoderTypeID == _kCFRuntimeNotATypeID) {
+ pthread_once(&once, &cmsEncoderClassInitialize);
+ }
+ return cmsEncoderTypeID;
+}
+
+/*
+ * Create a CMSEncoder. Result must eventually be freed via CFRelease().
+ */
+OSStatus CMSEncoderCreate(
+ CMSEncoderRef *cmsEncoderOut) /* RETURNED */
+{
+ CMSEncoderRef cmsEncoder = NULL;
+
+ uint32_t extra = sizeof(*cmsEncoder) - sizeof(cmsEncoder->base);
+ cmsEncoder = (CMSEncoderRef)_CFRuntimeCreateInstance(NULL, CMSEncoderGetTypeID(),
+ extra, NULL);
+ if(cmsEncoder == NULL) {
+ return errSecAllocate;
+ }
+ cmsEncoder->encState = ES_Init;
+ cmsEncoder->chainMode = kCMSCertificateChain;
+ *cmsEncoderOut = cmsEncoder;
+ return errSecSuccess;
+}
+
+#pragma mark --- Getters & Setters ---
+
+/*
+ * Specify signers of the CMS message; implies that the message will be signed.
+ */
+OSStatus CMSEncoderAddSigners(
+ CMSEncoderRef cmsEncoder,
+ CFTypeRef signerOrArray)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID());
+}
+
+/*
+ * Obtain an array of signers as specified in CMSEncoderSetSigners().
+ */
+OSStatus CMSEncoderCopySigners(
+ CMSEncoderRef cmsEncoder,
+ CFArrayRef *signers)
+{
+ if((cmsEncoder == NULL) || (signers == NULL)) {
+ return errSecParam;
+ }
+ if(cmsEncoder->signers != NULL) {
+ CFRetain(cmsEncoder->signers);
+ }
+ *signers = cmsEncoder->signers;
+ return errSecSuccess;
+}
+
+/*
+ * Specify recipients of the message. Implies that the message will be encrypted.
+ */
+OSStatus CMSEncoderAddRecipients(
+ CMSEncoderRef cmsEncoder,
+ CFTypeRef recipientOrArray)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ return cmsAppendToArray(recipientOrArray, &cmsEncoder->recipients,
+ SecCertificateGetTypeID());
+}
+
+/*
+ * Obtain an array of recipients as specified in CMSEncoderSetRecipients().
+ */
+OSStatus CMSEncoderCopyRecipients(
+ CMSEncoderRef cmsEncoder,
+ CFArrayRef *recipients)
+{
+ if((cmsEncoder == NULL) || (recipients == NULL)) {
+ return errSecParam;
+ }
+ if(cmsEncoder->recipients != NULL) {
+ CFRetain(cmsEncoder->recipients);
+ }
+ *recipients = cmsEncoder->recipients;
+ return errSecSuccess;
+}
+
+/*
+ * Specify additional certs to include in a signed message.
+ */
+OSStatus CMSEncoderAddSupportingCerts(
+ CMSEncoderRef cmsEncoder,
+ CFTypeRef certOrArray)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ return cmsAppendToArray(certOrArray, &cmsEncoder->otherCerts,
+ SecCertificateGetTypeID());
+}
+
+/*
+ * Obtain the SecCertificates provided in CMSEncoderAddSupportingCerts().
+ */
+OSStatus CMSEncoderCopySupportingCerts(
+ CMSEncoderRef cmsEncoder,
+ CFArrayRef *certs) /* RETURNED */
+{
+ if((cmsEncoder == NULL) || (certs == NULL)) {
+ return errSecParam;
+ }
+ if(cmsEncoder->otherCerts != NULL) {
+ CFRetain(cmsEncoder->otherCerts);
+ }
+ *certs = cmsEncoder->otherCerts;
+ return errSecSuccess;
+}
+
+OSStatus CMSEncoderSetHasDetachedContent(
+ CMSEncoderRef cmsEncoder,
+ Boolean detachedContent)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ cmsEncoder->detachedContent = detachedContent;
+ return errSecSuccess;
+}
+
+OSStatus CMSEncoderGetHasDetachedContent(
+ CMSEncoderRef cmsEncoder,
+ Boolean *detachedContent) /* RETURNED */
+{
+ if((cmsEncoder == NULL) || (detachedContent == NULL)) {
+ return errSecParam;
+ }
+ *detachedContent = cmsEncoder->detachedContent;
+ return errSecSuccess;
+}
+
+/*
+ * Optionally specify an eContentType OID for the inner EncapsulatedData for
+ * a signed message. The default eContentType, used of this function is not
+ * called, is id-data.
+ */
+OSStatus CMSEncoderSetEncapsulatedContentType(
+ CMSEncoderRef cmsEncoder,
+ const CSSM_OID *eContentType)
+{
+ if((cmsEncoder == NULL) || (eContentType == NULL)) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+
+ CSSM_OID *ecOid = &cmsEncoder->eContentType;
+ if(ecOid->Data != NULL) {
+ free(ecOid->Data);
+ }
+ cmsCopyCmsData(eContentType, ecOid);
+ return errSecSuccess;
+}
+
+OSStatus CMSEncoderSetEncapsulatedContentTypeOID(
+ CMSEncoderRef cmsEncoder,
+ CFTypeRef eContentTypeOID)
+{
+ // convert eContentTypeOID to a CSSM_OID
+ CSSM_OID contentType = { 0, NULL };
+ if (!eContentTypeOID || convertOid(eContentTypeOID, &contentType) != 0)
+ return errSecParam;
+ OSStatus result = CMSEncoderSetEncapsulatedContentType(cmsEncoder, &contentType);
+ if (contentType.Data)
+ free(contentType.Data);
+ return result;
+}
+
+/*
+ * Obtain the eContentType OID specified in CMSEncoderSetEncapsulatedContentType().
+ */
+OSStatus CMSEncoderCopyEncapsulatedContentType(
+ CMSEncoderRef cmsEncoder,
+ CFDataRef *eContentType)
+{
+ if((cmsEncoder == NULL) || (eContentType == NULL)) {
+ return errSecParam;
+ }
+
+ CSSM_OID *ecOid = &cmsEncoder->eContentType;
+ if(ecOid->Data == NULL) {
+ *eContentType = NULL;
+ }
+ else {
+ *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Optionally specify signed attributes. Only meaningful when creating a
+ * signed message. If this is called, it must be called before
+ * CMSEncoderUpdateContent().
+ */
+OSStatus CMSEncoderAddSignedAttributes(
+ CMSEncoderRef cmsEncoder,
+ CMSSignedAttributes signedAttributes)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ cmsEncoder->signedAttributes = signedAttributes;
+ return errSecSuccess;
+}
+
+/*
+ * Set the signing time for a CMSEncoder.
+ * This is only used if the kCMSAttrSigningTime attribute is included.
+ */
+OSStatus CMSEncoderSetSigningTime(
+ CMSEncoderRef cmsEncoder,
+ CFAbsoluteTime time)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ cmsEncoder->signingTime = time;
+ return errSecSuccess;
+}
+
+
+OSStatus CMSEncoderSetCertificateChainMode(
+ CMSEncoderRef cmsEncoder,
+ CMSCertificateChainMode chainMode)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ if(cmsEncoder->encState != ES_Init) {
+ return errSecParam;
+ }
+ switch(chainMode) {
+ case kCMSCertificateNone:
+ case kCMSCertificateSignerOnly:
+ case kCMSCertificateChain:
+ case kCMSCertificateChainWithRoot:
+ break;
+ default:
+ return errSecParam;
+ }
+ cmsEncoder->chainMode = chainMode;
+ return errSecSuccess;
+}
+
+OSStatus CMSEncoderGetCertificateChainMode(
+ CMSEncoderRef cmsEncoder,
+ CMSCertificateChainMode *chainModeOut)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+ *chainModeOut = cmsEncoder->chainMode;
+ return errSecSuccess;
+}
+
+void
+CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback)
+{
+ if (cmsEncoder->cmsMsg)
+ SecCmsMessageSetTSACallback(cmsEncoder->cmsMsg, tsaCallback);
+}
+
+void
+CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder, CFTypeRef tsaContext)
+{
+ if (cmsEncoder->cmsMsg)
+ SecCmsMessageSetTSAContext(cmsEncoder->cmsMsg, tsaContext);
+}
+
+#pragma mark --- Action ---
+
+/*
+ * Feed content bytes into the encoder.
+ * Can be called multiple times.
+ * No 'setter' routines can be called after this function has been called.
+ */
+OSStatus CMSEncoderUpdateContent(
+ CMSEncoderRef cmsEncoder,
+ const void *content,
+ size_t contentLen)
+{
+ if(cmsEncoder == NULL) {
+ return errSecParam;
+ }
+
+ OSStatus ortn = errSecSuccess;
+ switch(cmsEncoder->encState) {
+ case ES_Init:
+ /*
+ * First time thru: do the CmsMsg setup.
+ */
+ ortn = cmsSetupCmsMsg(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ /* fall thru to set up the encoder */
+
+ case ES_Msg:
+ /* We have a cmsMsg but no encoder; create one */
+ ASSERT(cmsEncoder->cmsMsg != NULL);
+ ASSERT(cmsEncoder->encoder == NULL);
+ ortn = cmsSetupEncoder(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ /* only legal calls now are update and finalize */
+ cmsEncoder->encState = ES_Updating;
+ break;
+
+ case ES_Updating:
+ ASSERT(cmsEncoder->encoder != NULL);
+ break;
+
+ case ES_Final:
+ /* Too late for another update */
+ return errSecParam;
+
+ default:
+ return errSecInternalComponent;
+ }
+
+ /* FIXME - CFIndex same size as size_t on 64bit? */
+ ortn = SecCmsEncoderUpdate(cmsEncoder->encoder, content, (CFIndex)contentLen);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsEncoderUpdate", ortn);
+ }
+ return ortn;
+}
+
+/*
+ * Finish encoding the message and obtain the encoded result.
+ * Caller must CFRelease the result.
+ */
+OSStatus CMSEncoderCopyEncodedContent(
+ CMSEncoderRef cmsEncoder,
+ CFDataRef *encodedContent)
+{
+ if((cmsEncoder == NULL) || (encodedContent == NULL)) {
+ return errSecParam;
+ }
+
+ OSStatus ortn;
+
+ switch(cmsEncoder->encState) {
+ case ES_Updating:
+ /* normal termination */
+ break;
+ case ES_Final:
+ /* already been called */
+ return errSecParam;
+ case ES_Msg:
+ case ES_Init:
+ /*
+ * The only time these are legal is when we're doing a SignedData
+ * with certificates only (no signers, no content).
+ */
+ if((cmsEncoder->signers != NULL) ||
+ (cmsEncoder->recipients != NULL) ||
+ (cmsEncoder->otherCerts == NULL)) {
+ return errSecParam;
+ }
+
+ /* Set up for certs only */
+ ortn = cmsSetupForSignedData(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ /* and an encoder */
+ ortn = cmsSetupEncoder(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ break;
+ }
+
+
+ ASSERT(cmsEncoder->encoder != NULL);
+ ortn = SecCmsEncoderFinish(cmsEncoder->encoder);
+ /* regardless of the outcome, the encoder itself has been freed */
+ cmsEncoder->encoder = NULL;
+ if(ortn) {
+ return cmsRtnToOSStatus(ortn);
+ }
+ cmsEncoder->encState = ES_Final;
+
+ if((cmsEncoder->encoderOut.Data == NULL) && !cmsEncoder->customCoder) {
+ /* not sure how this could happen... */
+ dprintf("Successful encode, but no data\n");
+ return errSecInternalComponent;
+ }
+ if(cmsEncoder->customCoder) {
+ /* we're done */
+ *encodedContent = NULL;
+ return errSecSuccess;
+ }
+
+ /* in two out of three cases, we're done */
+ switch(cmsEncoder->op) {
+ case EO_Sign:
+ case EO_Encrypt:
+ *encodedContent = CFDataCreate(NULL, (const UInt8 *)cmsEncoder->encoderOut.Data,
+ cmsEncoder->encoderOut.Length);
+ return errSecSuccess;
+ case EO_SignEncrypt:
+ /* proceed, more work to do */
+ break;
+ }
+
+ /*
+ * Signing & encrypting.
+ * Due to bugs in the libsecurity_smime encoder, it can't encode nested
+ * ContentInfos in one shot. So we do another pass, specifying the SignedData
+ * inside of the ContentInfo we just created as the data to encrypt.
+ */
+ SecAsn1CoderRef asn1Coder = NULL;
+ CSSM_DATA signedData = {0, NULL};
+
+ ortn = SecAsn1CoderCreate(&asn1Coder);
+ if(ortn) {
+ return ortn;
+ }
+ ortn = cmsContentInfoContent(asn1Coder, &cmsEncoder->encoderOut, &signedData);
+ if(ortn) {
+ goto errOut;
+ }
+
+ /* now just encrypt that, one-shot */
+ ortn = CMSEncode(NULL, /* no signers this time */
+ cmsEncoder->recipients,
+ &CSSMOID_PKCS7_SignedData, /* fake out encoder so it doesn't try to actually
+ * encode the signedData - this asserts the
+ * SEC_OID_OTHER OID tag in the EnvelopedData's
+ * ContentInfo */
+ FALSE, /* detachedContent */
+ kCMSAttrNone, /* signedAttributes - none this time */
+ signedData.Data, signedData.Length,
+ encodedContent);
+
+errOut:
+ if(asn1Coder) {
+ SecAsn1CoderRelease(asn1Coder);
+ }
+ return ortn;
+}
+
+#pragma mark --- High-level API ---
+
+/*
+ * High-level, one-shot encoder function.
+ */
+OSStatus CMSEncode(
+ CFTypeRef signers,
+ CFTypeRef recipients,
+ const CSSM_OID *eContentType,
+ Boolean detachedContent,
+ CMSSignedAttributes signedAttributes,
+ const void *content,
+ size_t contentLen,
+ CFDataRef *encodedContent) /* RETURNED */
+{
+ if((signers == NULL) && (recipients == NULL)) {
+ return errSecParam;
+ }
+ if(encodedContent == NULL) {
+ return errSecParam;
+ }
+
+ CMSEncoderRef cmsEncoder;
+ OSStatus ortn;
+
+ /* set up the encoder */
+ ortn = CMSEncoderCreate(&cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+
+ /* subsequent errors to errOut: */
+ if(signers) {
+ ortn = CMSEncoderAddSigners(cmsEncoder, signers);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ if(recipients) {
+ ortn = CMSEncoderAddRecipients(cmsEncoder, recipients);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ if(eContentType) {
+ ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ if(detachedContent) {
+ ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ if(signedAttributes) {
+ ortn = CMSEncoderAddSignedAttributes(cmsEncoder, signedAttributes);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ /* GO */
+ ortn = CMSEncoderUpdateContent(cmsEncoder, content, contentLen);
+ if(ortn) {
+ goto errOut;
+ }
+ ortn = CMSEncoderCopyEncodedContent(cmsEncoder, encodedContent);
+
+errOut:
+ CFRelease(cmsEncoder);
+ return ortn;
+}
+
+OSStatus CMSEncodeContent(
+ CFTypeRef signers,
+ CFTypeRef recipients,
+ CFTypeRef eContentTypeOID,
+ Boolean detachedContent,
+ CMSSignedAttributes signedAttributes,
+ const void *content,
+ size_t contentLen,
+ CFDataRef *encodedContentOut) /* RETURNED */
+{
+ // convert eContentTypeOID to a CSSM_OID
+ CSSM_OID contentType = { 0, NULL };
+ if (eContentTypeOID && convertOid(eContentTypeOID, &contentType) != 0)
+ return errSecParam;
+ const CSSM_OID *contentTypePtr = (eContentTypeOID) ? &contentType : NULL;
+ OSStatus result = CMSEncode(signers, recipients, contentTypePtr,
+ detachedContent, signedAttributes,
+ content, contentLen, encodedContentOut);
+ if (contentType.Data)
+ free(contentType.Data);
+ return result;
+}
+
+#pragma mark --- SPI routines declared in CMSPrivate.h ---
+
+/*
+ * Obtain the SecCmsMessageRef associated with a CMSEncoderRef.
+ * If we don't have a SecCmsMessageRef yet, we create one now.
+ * This is the only place where we go to state ES_Msg.
+ */
+OSStatus CMSEncoderGetCmsMessage(
+ CMSEncoderRef cmsEncoder,
+ SecCmsMessageRef *cmsMessage) /* RETURNED */
+{
+ if((cmsEncoder == NULL) || (cmsMessage == NULL)) {
+ return errSecParam;
+ }
+ if(cmsEncoder->cmsMsg != NULL) {
+ ASSERT(cmsEncoder->encState != ES_Init);
+ *cmsMessage = cmsEncoder->cmsMsg;
+ return errSecSuccess;
+ }
+
+ OSStatus ortn = cmsSetupCmsMsg(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ *cmsMessage = cmsEncoder->cmsMsg;
+
+ /* Don't set up encoder yet; caller might do that via CMSEncoderSetEncoder */
+ cmsEncoder->encState = ES_Msg;
+ return errSecSuccess;
+}
+
+/*
+ * Optionally specify a SecCmsEncoderRef to use with a CMSEncoderRef.
+ * If this is called, it must be called before the first call to
+ * CMSEncoderUpdateContent(). The CMSEncoderRef takes ownership of the
+ * incoming SecCmsEncoderRef.
+ */
+OSStatus CMSEncoderSetEncoder(
+ CMSEncoderRef cmsEncoder,
+ SecCmsEncoderRef encoder)
+{
+ if((cmsEncoder == NULL) || (encoder == NULL)) {
+ return errSecParam;
+ }
+
+ OSStatus ortn;
+
+ switch(cmsEncoder->encState) {
+ case ES_Init:
+ /* No message, no encoder */
+ ASSERT(cmsEncoder->cmsMsg == NULL);
+ ASSERT(cmsEncoder->encoder == NULL);
+ ortn = cmsSetupCmsMsg(cmsEncoder);
+ if(ortn) {
+ return ortn;
+ }
+ /* drop thru to set encoder */
+ case ES_Msg:
+ /* cmsMsg but no encoder */
+ ASSERT(cmsEncoder->cmsMsg != NULL);
+ ASSERT(cmsEncoder->encoder == NULL);
+ cmsEncoder->encoder = encoder;
+ cmsEncoder->encState = ES_Updating;
+ cmsEncoder->customCoder = true; /* we won't see data */
+ return errSecSuccess;
+ default:
+ /* no can do, too late */
+ return errSecParam;
+ }
+}
+
+/*
+ * Obtain the SecCmsEncoderRef associated with a CMSEncoderRef.
+ * Returns a NULL SecCmsEncoderRef if neither CMSEncoderSetEncoder nor
+ * CMSEncoderUpdateContent() has been called.
+ * The CMSEncoderRef retains ownership of the SecCmsEncoderRef.
+ */
+OSStatus CMSEncoderGetEncoder(
+ CMSEncoderRef cmsEncoder,
+ SecCmsEncoderRef *encoder) /* RETURNED */
+{
+ if((cmsEncoder == NULL) || (encoder == NULL)) {
+ return errSecParam;
+ }
+
+ /* any state, whether we have an encoder or not is OK */
+ *encoder = cmsEncoder->encoder;
+ return errSecSuccess;
+}
+
+#include <AssertMacros.h>
+
+/*
+ * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
+ * present. This timestamp is an authenticated timestamp provided by
+ * a timestamping authority.
+ *
+ * Returns errSecParam if the CMS message was not signed or if signerIndex
+ * is greater than the number of signers of the message minus one.
+ *
+ * This cannot be called until after CMSEncoderCopyEncodedContent() is called.
+ */
+OSStatus CMSEncoderCopySignerTimestamp(
+ CMSEncoderRef cmsEncoder,
+ size_t signerIndex, /* usually 0 */
+ CFAbsoluteTime *timestamp) /* RETURNED */
+{
+ return CMSEncoderCopySignerTimestampWithPolicy(
+ cmsEncoder,
+ NULL,
+ signerIndex,
+ timestamp);
+}
+
+OSStatus CMSEncoderCopySignerTimestampWithPolicy(
+ CMSEncoderRef cmsEncoder,
+ CFTypeRef timeStampPolicy,
+ size_t signerIndex, /* usually 0 */
+ CFAbsoluteTime *timestamp) /* RETURNED */
+{
+ OSStatus status = errSecParam;
+ SecCmsMessageRef cmsg;
+ SecCmsSignedDataRef signedData = NULL;
+ int numContentInfos = 0;
+
+ require(cmsEncoder && timestamp, xit);
+ require_noerr(CMSEncoderGetCmsMessage(cmsEncoder, &cmsg), xit);
+ numContentInfos = SecCmsMessageContentLevelCount(cmsg);
+ for (int dex = 0; !signedData && dex < numContentInfos; dex++)
+ {
+ SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
+ SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
+ if (tag == SEC_OID_PKCS7_SIGNED_DATA)
+ if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
+ if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
+ {
+ status = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp);
+ break;
+ }
+ }
+
+xit:
+ return status;
+}