CMSDecoder.cpp   [plain text]


/*
 * Copyright (c) 2006-2010 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@
 */

/*
 * CMSDecoder.cpp - Interface for decoding CMS messages.
 */

#include "CMSDecoder.h"
#include "CMSPrivate.h"
#include "CMSUtils.h"
#include <../libsecurity_codesigning/lib/csutilities.h>

#include <Security/SecCmsDecoder.h>
#include <Security/SecCmsEnvelopedData.h>
#include <Security/SecCmsMessage.h>
#include <Security/SecCmsSignedData.h>
#include <Security/SecCmsSignerInfo.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsDigestContext.h>
#include <Security/SecCertificate.h>
#include <Security/SecSMIME.h>
#include <Security/oidsattr.h>
#include <Security/SecTrustPriv.h>
#include <CoreFoundation/CFRuntime.h>
#include <pthread.h>
#include <AssertMacros.h>

#pragma mark --- Private types and definitions ---

/*
 * Decoder state.
 */
typedef enum {
	DS_Init,		/* between CMSDecoderCreate and CMSDecoderUpdateMessage */
	DS_Updating,	/* between first CMSDecoderUpdateMessage and CMSDecoderFinalizeMessage */
	DS_Final		/* CMSDecoderFinalizeMessage has been called */
} CMSDecoderState;

/*
 * Caller's CMSDecoderRef points to one of these.
 */
struct _CMSDecoder {
	CFRuntimeBase		base;
	CMSDecoderState		decState;
	SecArenaPoolRef		arena;				/* the decoder's arena */
	SecCmsDecoderRef	decoder;
	CFDataRef			detachedContent;
	CFTypeRef			keychainOrArray;	/* from CMSDecoderSetSearchKeychain() */
	
	/*
	 * The following are valid (and quiescent) after CMSDecoderFinalizeMessage().
	 */
	SecCmsMessageRef	cmsMsg;
	Boolean				wasEncrypted;	/* valid after CMSDecoderFinalizeMessage() */
	SecCmsSignedDataRef	signedData;		/* if there is one... */
	/* only non-NULL if we found a signedData */
	size_t				numSigners;
	CSSM_OID			*eContentType;
	/* etc. */
};

static void cmsDecoderInit(CFTypeRef dec);
static void cmsDecoderFinalize(CFTypeRef dec);

static CFRuntimeClass cmsDecoderRuntimeClass =
{
	0,			/* version */
	"CMSDecoder",
	cmsDecoderInit,
	NULL,		/* copy */
	cmsDecoderFinalize,
	NULL,		/* equal - just use pointer equality */
	NULL,		/* hash, ditto */
	NULL,		/* copyFormattingDesc */
	NULL		/* copyDebugDesc */
};

#pragma mark --- Private Routines ---

static CFTypeID cmsDecoderTypeID = _kCFRuntimeNotATypeID;

/* one time only class init, called via pthread_once() in CMSDecoderGetTypeID() */
static void cmsDecoderClassInitialize(void)
{
	cmsDecoderTypeID =
    _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsDecoderRuntimeClass);
}

/* init called out from _CFRuntimeCreateInstance() */
static void cmsDecoderInit(CFTypeRef dec)
{
	char *start = ((char *)dec) + sizeof(CFRuntimeBase);
	memset(start, 0, sizeof(struct _CMSDecoder) - sizeof(CFRuntimeBase));
}

/*
 * Dispose of a CMSDecoder. Called out from CFRelease().
 */
static void cmsDecoderFinalize(
                               CFTypeRef		dec)
{
	CMSDecoderRef cmsDecoder = (CMSDecoderRef)dec;
	if(cmsDecoder == NULL) {
		return;
	}
	if(cmsDecoder->decoder != NULL) {
		/*
		 * Normally this gets freed in SecCmsDecoderFinish - this is
		 * an error case.
		 * FIXME: SecCmsDecoderDestroy() appears to destroy the
		 * cmsMsg too! Plus there's a comment there re: a leak...
		 */
		SecCmsDecoderDestroy(cmsDecoder->decoder);
	}
	CFRELEASE(cmsDecoder->detachedContent);
	CFRELEASE(cmsDecoder->keychainOrArray);
	if(cmsDecoder->cmsMsg != NULL) {
		SecCmsMessageDestroy(cmsDecoder->cmsMsg);
	}
	if(cmsDecoder->arena != NULL) {
		SecArenaPoolFree(cmsDecoder->arena, false);
	}
}


/*
 * Given detached content and a valid (decoded) SignedData, digest the detached
 * content. This occurs at the later of {CMSDecoderFinalizeMessage() finding a
 * SignedData when already have detachedContent, or CMSDecoderSetDetachedContent()
 * when we already have a SignedData).
 */
static OSStatus cmsDigestDetachedContent(
                                         CMSDecoderRef cmsDecoder)
{
	ASSERT((cmsDecoder->signedData != NULL) && (cmsDecoder->detachedContent != NULL));
	
	SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(cmsDecoder->signedData);
	if(digestAlgorithms == NULL) {
		return errSecUnknownFormat;
	}
	SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms);
	if(digcx == NULL) {
		return errSecAllocate;
	}
	CSSM_DATA **digests = NULL;
	
	SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(cmsDecoder->detachedContent),
                              CFDataGetLength(cmsDecoder->detachedContent));
	/* note this frees the digest content regardless */
	OSStatus ortn = SecCmsDigestContextFinishMultiple(digcx, cmsDecoder->arena, &digests);
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn);
		CSSM_PERROR("SecCmsDigestContextFinishMultiple", ortn);
		return ortn;
	}
	ortn = SecCmsSignedDataSetDigests(cmsDecoder->signedData, digestAlgorithms, digests);
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn);
		CSSM_PERROR("SecCmsSignedDataSetDigests", ortn);
	}
	return ortn;
}

#pragma mark --- Start of Public API ---

CFTypeID CMSDecoderGetTypeID(void)
{
	static pthread_once_t once = PTHREAD_ONCE_INIT;
	
	if(cmsDecoderTypeID == _kCFRuntimeNotATypeID) {
		pthread_once(&once, &cmsDecoderClassInitialize);
	}
	return cmsDecoderTypeID;
}

/*
 * Create a CMSDecoder. Result must eventually be freed via CFRelease().
 */
OSStatus CMSDecoderCreate(
                          CMSDecoderRef		*cmsDecoderOut)	/* RETURNED */
{
	CMSDecoderRef cmsDecoder = NULL;
    
	uint32_t extra = sizeof(*cmsDecoder) - sizeof(cmsDecoder->base);
	cmsDecoder = (CMSDecoderRef)_CFRuntimeCreateInstance(NULL, CMSDecoderGetTypeID(),
                                                         extra, NULL);
	if(cmsDecoder == NULL) {
		return errSecAllocate;
	}
	cmsDecoder->decState = DS_Init;
	*cmsDecoderOut = cmsDecoder;
	return errSecSuccess;
}

/*
 * Feed raw bytes of the message to be decoded into the decoder. Can be called
 * multiple times.
 */
OSStatus CMSDecoderUpdateMessage(
                                 CMSDecoderRef		cmsDecoder,
                                 const void			*msgBytes,
                                 size_t				msgBytesLen)
{
	if(cmsDecoder == NULL) {
		return errSecParam;
	}
	
	OSStatus ortn;
	switch(cmsDecoder->decState) {
		case DS_Init:
			/* First time through; set up */
			ASSERT(cmsDecoder->decoder == NULL);
			ASSERT(cmsDecoder->arena == NULL);
			ortn = SecArenaPoolCreate(1024, &cmsDecoder->arena);
			if(ortn) {
				return cmsRtnToOSStatus(ortn);
			}
			ortn = SecCmsDecoderCreate(cmsDecoder->arena,
                                       NULL, NULL, NULL, NULL, NULL, NULL, &cmsDecoder->decoder);
			if(ortn) {
				ortn = cmsRtnToOSStatus(ortn);
				CSSM_PERROR("SecCmsDecoderCreate", ortn);
				return ortn;
			}
			cmsDecoder->decState = DS_Updating;
			break;
            
		case DS_Updating:
			ASSERT(cmsDecoder->decoder != NULL);
			break;
			
		case DS_Final:
			/* Too late for another update */
			return errSecParam;
			
		default:
			dprintf("CMSDecoderUpdateMessage: bad decState\n");
			return errSecInternalComponent;
	}
	
	/* FIXME - CFIndex same size as size_t on 64bit? */
	ortn = SecCmsDecoderUpdate(cmsDecoder->decoder, msgBytes, (CFIndex)msgBytesLen);
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat);
		CSSM_PERROR("SecCmsDecoderUpdate", ortn);
	}
	return ortn;
}

/*
 * Indicate that no more CMSDecoderUpdateMessage() calls are forthcoming;
 * finish decoding the message. We parse the message as best we can, up to
 * but not including verifying individual signerInfos.
 */
OSStatus CMSDecoderFinalizeMessage(
                                   CMSDecoderRef		cmsDecoder)
{
	if(cmsDecoder == NULL) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Updating) {
		return errSecParam;
	}
	ASSERT(cmsDecoder->decoder != NULL);
	OSStatus ortn = SecCmsDecoderFinish(cmsDecoder->decoder, &cmsDecoder->cmsMsg);
	cmsDecoder->decState = DS_Final;
	
	/* SecCmsDecoderFinish destroyed the decoder even on failure */
	cmsDecoder->decoder = NULL;
	
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat);
		CSSM_PERROR("SecCmsDecoderFinish", ortn);
		return ortn;
	}
	
	ASSERT(cmsDecoder->cmsMsg != NULL);
	cmsDecoder->wasEncrypted = SecCmsMessageIsEncrypted(cmsDecoder->cmsMsg);
	
	/* Look for a SignedData */
	int numContentInfos = SecCmsMessageContentLevelCount(cmsDecoder->cmsMsg);
	int dex;
	for(dex=0; dex<numContentInfos; dex++) {
		SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsDecoder->cmsMsg, dex);
		SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
		switch(tag) {
			case SEC_OID_PKCS7_SIGNED_DATA:
				cmsDecoder->signedData =
                (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci);
				/* dig down one more layer for eContentType */
				ci = SecCmsSignedDataGetContentInfo(cmsDecoder->signedData);
				cmsDecoder->eContentType = SecCmsContentInfoGetContentTypeOID(ci);
				break;
			default:
				break;
		}
		if(cmsDecoder->signedData != NULL) {
			break;
		}
        
	}
	
	/* minimal processing of optional signedData... */
	if(cmsDecoder->signedData != NULL) {
		cmsDecoder->numSigners = (size_t)
        SecCmsSignedDataSignerInfoCount(cmsDecoder->signedData);
		if(cmsDecoder->detachedContent != NULL) {
			/* time to calculate digests from detached content */
			ortn = cmsDigestDetachedContent(cmsDecoder);
		}
	}
	return ortn;
}

/*
 * A signed CMS message optionally includes the data which was signed. If the
 * message does not include the signed data, caller specifies the signed data
 * (the "detached content") here.
 *
 * This can be called either before or after the actual decoding of the message
 * (via CMSDecoderUpdateMessage() and CMSDecoderFinalizeMessage()); the only
 * restriction is that, if detached content is required, this function must
 * be called befoere successfully ascertaining the signature status via
 * CMSDecoderCopySignerStatus().
 */
OSStatus CMSDecoderSetDetachedContent(
                                      CMSDecoderRef		cmsDecoder,
                                      CFDataRef			detachedContent)
{
	if((cmsDecoder == NULL) || (detachedContent == NULL)) {
		return errSecParam;
	}
	cmsDecoder->detachedContent = detachedContent;
	CFRetain(detachedContent);
	
	if(cmsDecoder->signedData != NULL) {
		/* time to calculate digests from detached content */
		ASSERT(cmsDecoder->decState == DS_Final);
		return cmsDigestDetachedContent(cmsDecoder);
	}
	return errSecSuccess;
}

/*
 * Obtain the detached content specified in CMSDecoderSetDetachedContent().
 * Returns a NULL detachedContent if no detached content has been specified.
 * Caller must CFRelease() the result.
 */
OSStatus CMSDecoderCopyDetachedContent(
                                       CMSDecoderRef		cmsDecoder,
                                       CFDataRef			*detachedContent)		/* RETURNED */
{
	if((cmsDecoder == NULL) || (detachedContent == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->detachedContent != NULL) {
		CFRetain(cmsDecoder->detachedContent);
	}
	*detachedContent = cmsDecoder->detachedContent;
	return errSecSuccess;
}

/*
 * Optionally specify a SecKeychainRef, or an array of them, containing
 * intermediate certs to be used in verifying a signed message's signer
 * certs. By default, the default keychain search list is used for this.
 * Specify an empty CFArrayRef to search *no* keychains for intermediate
 * certs.
 * IF this is called, it must be called before CMSDecoderCopySignerStatus().
 */
OSStatus CMSDecoderSetSearchKeychain(
                                     CMSDecoderRef		cmsDecoder,
                                     CFTypeRef			keychainOrArray)
{
	if(cmsDecoder == NULL) {
		return errSecParam;
	}
	cmsDecoder->keychainOrArray = keychainOrArray;
	if(keychainOrArray) {
		CFRetain(keychainOrArray);
	}
	return errSecSuccess;
}

/*
 * Obtain the number of signers of a message. A result of zero indicates that
 * the message was not signed.
 */
OSStatus CMSDecoderGetNumSigners(
                                 CMSDecoderRef		cmsDecoder,
                                 size_t				*numSigners)			/* RETURNED */
{
	if((cmsDecoder == NULL) || (numSigners == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Final) {
		return errSecParam;
	}
	*numSigners = cmsDecoder->numSigners;
	return errSecSuccess;
}

/*
 * Obtain the status of a CMS message's signature. A CMS message can
 * be signed my multiple signers; this function returns the status
 * associated with signer 'n' as indicated by the signerIndex parameter.
 */
OSStatus CMSDecoderCopySignerStatus(
                                    CMSDecoderRef		cmsDecoder,
                                    size_t				signerIndex,
                                    CFTypeRef			policyOrArray,
                                    Boolean				evaluateSecTrust,
                                    CMSSignerStatus		*signerStatus,			/* optional; RETURNED */
                                    SecTrustRef			*secTrust,				/* optional; RETURNED */
                                    OSStatus			*certVerifyResultCode)	/* optional; RETURNED */
{
	if((cmsDecoder == NULL) || (cmsDecoder->decState != DS_Final)) {
		return errSecParam;
	}
	
	/* initialize return values */
	if(signerStatus) {
		*signerStatus = kCMSSignerUnsigned;
	}
	if(secTrust) {
		*secTrust = NULL;
	}
	if(certVerifyResultCode) {
		*certVerifyResultCode = 0;
	}
	
	if(cmsDecoder->signedData == NULL) {
		*signerStatus = kCMSSignerUnsigned;	/* redundant, I know, but explicit */
		return errSecSuccess;
	}
	ASSERT(cmsDecoder->numSigners > 0);
	if(signerIndex >= cmsDecoder->numSigners) {
		*signerStatus = kCMSSignerInvalidIndex;
		return errSecSuccess;
	}
	if(!SecCmsSignedDataHasDigests(cmsDecoder->signedData)) {
		*signerStatus = kCMSSignerNeedsDetachedContent;
		return errSecSuccess;
	}
	
	/*
	 * OK, we should be able to verify this signerInfo.
	 * I think we have to do the SecCmsSignedDataVerifySignerInfo first
	 * in order get all the cert pieces into place before returning them
	 * to the caller.
	 */
	SecTrustRef theTrust = NULL;
	OSStatus vfyRtn = SecCmsSignedDataVerifySignerInfo(cmsDecoder->signedData,
                                                       (int)signerIndex,
                                                       /*
                                                        * FIXME this cast should not be necessary, but libsecurity_smime
                                                        * declares this argument as a SecKeychainRef
                                                        */
                                                       (SecKeychainRef)cmsDecoder->keychainOrArray,
                                                       policyOrArray,
                                                       &theTrust);
    /* Subsequent errors to errOut: */
    
	/*
	 * NOTE the smime lib did NOT evaluate that SecTrust - it only does
	 * SecTrustEvaluate() if we don't ask for a copy.
	 *
	 * FIXME deal with multitudes of status returns here...for now, proceed with
	 * obtaining components the caller wants and assume that a nonzero vfyRtn
	 * means "bad signature".
	 */
	OSStatus ortn = errSecSuccess;
	SecTrustResultType secTrustResult;
	CSSM_RETURN tpVfyStatus = CSSM_OK;
	OSStatus evalRtn;
	
	if(secTrust != NULL) {
		*secTrust = theTrust;
		/* we'll release our reference at the end */
		if (theTrust)
			CFRetain(theTrust);
	}
	SecCmsSignerInfoRef signerInfo =
    SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
	if(signerInfo == NULL) {
		/* should never happen */
		ASSERT(0);
		dprintf("CMSDecoderCopySignerStatus: no signerInfo\n");
		ortn = errSecInternalComponent;
		goto errOut;
	}
    
	/* now do the actual cert verify */
	if(evaluateSecTrust) {
		evalRtn = SecTrustEvaluate(theTrust, &secTrustResult);
		if(evalRtn) {
			/* should never happen */
			CSSM_PERROR("SecTrustEvaluate", evalRtn);
			dprintf("CMSDecoderCopySignerStatus: SecTrustEvaluate error\n");
			ortn = errSecInternalComponent;
			goto errOut;
		}
		switch(secTrustResult) {
			case kSecTrustResultUnspecified:
				/* cert chain valid, no special UserTrust assignments */
			case kSecTrustResultProceed:
				/* cert chain valid AND user explicitly trusts this */
				break;
			case kSecTrustResultDeny:
				tpVfyStatus = CSSMERR_APPLETP_TRUST_SETTING_DENY;
				break;
			case kSecTrustResultConfirm:
				dprintf("SecTrustEvaluate reported confirm\n");
				tpVfyStatus = CSSMERR_TP_NOT_TRUSTED;
				break;
			default:
			{
				/* get low-level TP error */
				OSStatus tpStatus;
				ortn = SecTrustGetCssmResultCode(theTrust, &tpStatus);
				if(ortn) {
					CSSM_PERROR("SecTrustGetCssmResultCode", ortn);
				}
				else {
					tpVfyStatus = tpStatus;
				}
				CSSM_PERROR("TP status after SecTrustEvaluate", tpVfyStatus);
				break;
			}
		} 	/* switch(secTrustResult) */
	}		/* evaluateSecTrust true */
	if(certVerifyResultCode != NULL) {
		*certVerifyResultCode = tpVfyStatus;
	}
	
	/* cook up global status based on vfyRtn and tpVfyStatus */
	if(signerStatus != NULL) {
		if((vfyRtn == errSecSuccess) && (tpVfyStatus == CSSM_OK))  {
			*signerStatus = kCMSSignerValid;
		}
		else if(vfyRtn != errSecSuccess) {
			/* this could mean other things, but for now... */
			*signerStatus = kCMSSignerInvalidSignature;
		}
		else {
			*signerStatus = kCMSSignerInvalidCert;
		}
	}
errOut:
	CFRELEASE(theTrust);
	return ortn;
}

/*
 * Obtain the email address of signer 'signerIndex' of a CMS message, if
 * present.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopySignerEmailAddress(
                                          CMSDecoderRef		cmsDecoder,
                                          size_t				signerIndex,
                                          CFStringRef			*signerEmailAddress)	/* RETURNED */
{
	if((cmsDecoder == NULL) ||
	   (signerEmailAddress == NULL) ||
	   (cmsDecoder->signedData == NULL) ||			/* not signed */
	   (signerIndex >= cmsDecoder->numSigners) ||	/* index out of range */
	   (cmsDecoder->decState != DS_Final)) {
		return errSecParam;
	}
	
	SecCmsSignerInfoRef signerInfo =
    SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
	if(signerInfo == NULL) {
		/* should never happen */
		ASSERT(0);
		dprintf("CMSDecoderCopySignerEmailAddress: no signerInfo\n");
		return errSecInternalComponent;
	}
	
	/*
	 * This is leaking memory in libsecurityKeychain per Radar 4412699.
	 */
	*signerEmailAddress = SecCmsSignerInfoGetSignerEmailAddress(signerInfo);
	return errSecSuccess;
}

/*
 * Obtain the certificate of signer 'signerIndex' of a CMS message, if
 * present.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopySignerCert(
                                  CMSDecoderRef		cmsDecoder,
                                  size_t				signerIndex,
                                  SecCertificateRef	*signerCert)			/* RETURNED */
{
	if((cmsDecoder == NULL) ||
	   (signerCert == NULL) ||
	   (cmsDecoder->signedData == NULL) ||			/* not signed */
	   (signerIndex >= cmsDecoder->numSigners) ||	/* index out of range */
	   (cmsDecoder->decState != DS_Final)) {
		return errSecParam;
	}
    
	SecCmsSignerInfoRef signerInfo =
    SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
	if(signerInfo == NULL) {
		/* should never happen */
		ASSERT(0);
		dprintf("CMSDecoderCopySignerCertificate: no signerInfo\n");
		return errSecInternalComponent;
	}
	*signerCert = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL);
	/* libsecurity_smime does NOT retain that */
	if(*signerCert == NULL) {
		/* should never happen */
		ASSERT(0);
		dprintf("CMSDecoderCopySignerCertificate: no signerCert\n");
		return errSecInternalComponent;
	}
	CFRetain(*signerCert);
	return errSecSuccess;
}

/*
 * Determine whether a CMS message was encrypted, and if so, whether we were
 * able to decrypt it.
 */
OSStatus CMSDecoderIsContentEncrypted(
                                      CMSDecoderRef	cmsDecoder,
                                      Boolean			*wasEncrypted)
{
	if((cmsDecoder == NULL) || (wasEncrypted == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Final) {
		return errSecParam;
	}
	*wasEncrypted = cmsDecoder->wasEncrypted;
	return errSecSuccess;
}

/*
 * Obtain the eContentType OID for a SignedData's EncapsulatedContentType, if
 * present.
 */
OSStatus CMSDecoderCopyEncapsulatedContentType(
                                               CMSDecoderRef		cmsDecoder,
                                               CFDataRef			*eContentType)		/* RETURNED */
{
	if((cmsDecoder == NULL) || (eContentType == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Final) {
		return errSecParam;
	}
	if(cmsDecoder->signedData == NULL) {
		*eContentType = NULL;
	}
	else {
		CSSM_OID *ecOid = cmsDecoder->eContentType;
		*eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
	}
	return errSecSuccess;
}

/*
 * Obtain an array of all of the certificates in a message. Elements of the
 * returned array are SecCertificateRefs. The caller must CFRelease the returned
 * array.
 * This cannot be called until after CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopyAllCerts(
                                CMSDecoderRef		cmsDecoder,
                                CFArrayRef			*certs)					/* RETURNED */
{
	if((cmsDecoder == NULL) || (certs == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Final) {
		return errSecParam;
	}
	if(cmsDecoder->signedData == NULL) {
		/* message wasn't signed */
		*certs = NULL;
		return errSecSuccess;
	}
	
	/* NULL_terminated array of CSSM_DATA ptrs */
	CSSM_DATA_PTR *cssmCerts = SecCmsSignedDataGetCertificateList(cmsDecoder->signedData);
	if((cssmCerts == NULL) || (*cssmCerts == NULL)) {
		*certs = NULL;
		return errSecSuccess;
	}
	
	CFMutableArrayRef allCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	CSSM_DATA_PTR *cssmCert;
	for(cssmCert=cssmCerts; *cssmCert!=NULL; cssmCert++) {
		OSStatus ortn;
		SecCertificateRef cfCert;
		ortn = SecCertificateCreateFromData(*cssmCert,
                                            CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
                                            &cfCert);
		if(ortn) {
			CFRelease(allCerts);
			return ortn;
		}
		CFArrayAppendValue(allCerts, cfCert);
		/* the array holds the only needed refcount */
		CFRelease(cfCert);
	}
	*certs = allCerts;
	return errSecSuccess;
}

/*
 * Obtain the actual message content (payload), if any. If the message was
 * signed with detached content this will return NULL.
 * Caller must CFRelease the result.
 */
OSStatus CMSDecoderCopyContent(
                               CMSDecoderRef		cmsDecoder,
                               CFDataRef			*content)				/* RETURNED */
{
	if((cmsDecoder == NULL) || (content == NULL)) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Final) {
		return errSecParam;
	}
	if(cmsDecoder->cmsMsg == NULL) {
		/* Hmmm....looks like the finalize call failed */
		return errSecParam;
	}
	CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsDecoder->cmsMsg);
	if((odata == NULL) || (odata->Length == 0)) {
		/* i.e., detached content */
		*content = NULL;
		return errSecSuccess;
	}
	*content = CFDataCreate(NULL, (const UInt8 *)odata->Data, odata->Length);
	return errSecSuccess;
}

#pragma mark --- SPI declared in CMSPrivate.h ---

/*
 * Obtain the SecCmsMessageRef associated with a CMSDecoderRef. Intended
 * to be called after decoding the message (i.e., after
 * CMSDecoderFinalizeMessage() to gain finer access to the contents of the
 * SecCmsMessageRef than is otherwise available via the CMSDecoder interface.
 * Returns a NULL SecCmsMessageRef if CMSDecoderFinalizeMessage() has not been
 * called.
 *
 * The CMSDecoder retains ownership of the returned SecCmsMessageRef.
 */
OSStatus CMSDecoderGetCmsMessage(
                                 CMSDecoderRef		cmsDecoder,
                                 SecCmsMessageRef	*cmsMessage)		/* RETURNED */
{
	if((cmsDecoder == NULL) || (cmsMessage == NULL)) {
		return errSecParam;
	}
	/* any state, whether we have a msg or not is OK */
	*cmsMessage = cmsDecoder->cmsMsg;
	return errSecSuccess;
}

/*
 * Optionally specify a SecCmsDecoderRef to use with a CMSDecoderRef.
 * If this is called, it must be called before the first call to
 * CMSDecoderUpdateMessage(). The CMSDecoderRef takes ownership of the
 * incoming SecCmsDecoderRef.
 */
OSStatus CMSDecoderSetDecoder(
                              CMSDecoderRef		cmsDecoder,
                              SecCmsDecoderRef	decoder)
{
	if((cmsDecoder == NULL) || (decoder == NULL)) {
		return errSecParam;
	}
	switch(cmsDecoder->decState) {
		case DS_Init:
			ASSERT(cmsDecoder->decoder == NULL);
			cmsDecoder->decoder = decoder;
			cmsDecoder->decState = DS_Updating;
			return errSecSuccess;
		case DS_Updating:
		case DS_Final:
			return errSecParam;
	}
	return errSecSuccess;
}

/*
 * Obtain the SecCmsDecoderRef associated with a CMSDecoderRef.
 * Returns a NULL SecCmsDecoderRef if neither CMSDecoderSetDecoder() nor
 * CMSDecoderUpdateMessage() has been called.
 * The CMSDecoderRef retains ownership of the SecCmsDecoderRef.
 */
OSStatus CMSDecoderGetDecoder(
                              CMSDecoderRef		cmsDecoder,
                              SecCmsDecoderRef	*decoder)			/* RETURNED */
{
	if((cmsDecoder == NULL) || (decoder == NULL)) {
		return errSecParam;
	}
	/* any state, whether we have a decoder or not is OK */
	*decoder = cmsDecoder->decoder;
	return errSecSuccess;
}

/*
 * Obtain the signing time of signer 'signerIndex' of a CMS message, if
 * present. This is an unauthenticate time, although it is part of the
 * signed attributes of the message.
 *
 * 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 CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopySignerSigningTime(
                                         CMSDecoderRef		cmsDecoder,
                                         size_t				signerIndex,            /* usually 0 */
                                         CFAbsoluteTime      *signingTime)			/* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    
    require(cmsDecoder && signingTime, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &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 = SecCmsSignerInfoGetSigningTime(signerInfo, signingTime);
                    break;
                }
    }
xit:
    return status;
}

/*
 * 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 CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopySignerTimestamp(
                                       CMSDecoderRef		cmsDecoder,
                                       size_t				signerIndex,        /* usually 0 */
                                       CFAbsoluteTime      *timestamp)			/* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    
    require(cmsDecoder && timestamp, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &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 = SecCmsSignerInfoGetTimestampTime(signerInfo, timestamp);
                    break;
                }
    }
    
xit:
    return status;
}

/*
 * Obtain an array of the certificates in a timestamp response. Elements of the
 * returned array are SecCertificateRefs. The caller must CFRelease the returned
 * array. 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. It returns
 * errSecItemNotFound if no certificates were found.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called. 
 */
OSStatus CMSDecoderCopySignerTimestampCertificates(
                                                   CMSDecoderRef		cmsDecoder,
                                                   size_t				signerIndex,            /* usually 0 */
                                                   CFArrayRef          *certificateRefs)       /* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg = NULL;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    CFIndex tsn = 0;
    bool good = false;
    
    require(cmsDecoder && certificateRefs, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &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))
                {
                    CFArrayRef certList = SecCmsSignerInfoGetTimestampCertList(signerInfo);
                    require_action(certList, xit, status = errSecItemNotFound);
                    CFMutableArrayRef certs = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(certList), certList);
                    
                    if(certs){
                        //reorder certificates:
                        tsn = CFArrayGetCount(certs);
                        good = tsn > 0 && Security::CodeSigning::isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(certs, tsn-1)));
                        
                        if ( good == false )
                        {
                            //change TS certificate ordering.
                            for (CFIndex n = 0; n < tsn; n++)
                            {
                                if (SecCertificateRef tsRoot = SecCertificateRef(CFArrayGetValueAtIndex(certs, n)))
                                    if ((good = Security::CodeSigning::isAppleCA(tsRoot))) {
                                        CFArrayExchangeValuesAtIndices(certs, n, tsn-1);
                                        break;
                                    }
                            }
                        }
                        
                        *certificateRefs = CFArrayCreateCopy(kCFAllocatorDefault, certs);
                        CFRelease(certs);
                        status = errSecSuccess;
                    }
                    break;
                }
    }
    
    
    xit:
        return status;
    }