/*
 * 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;
}
Example #2
0
OSStatus cmsNullWrapKey(SecKeyRef refKey,
                               CSSM_KEY_PTR rawKey)
{
    CSSM_DATA descData = {0, 0};
    CSSM_RETURN crtn;
    CSSM_CC_HANDLE ccHand;
    CSSM_ACCESS_CREDENTIALS creds;
    CSSM_CSP_HANDLE refCspHand = CSSM_INVALID_HANDLE;
    const CSSM_KEY *cssmKey = NULL;
    uint32 keyAttr;

    memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
    memset(rawKey, 0, sizeof(CSSM_KEY));

    crtn = SecKeyGetCSSMKey(refKey, &cssmKey);
    if(crtn) {
        CSSM_PERROR("SecKeyGetCSSMKey", crtn);
        goto loser;
    }
    crtn = SecKeyGetCSPHandle(refKey, &refCspHand);
    if(crtn) {
        CSSM_PERROR("SecKeyGetCSPHandle", crtn);
        goto loser;
    }

    crtn = CSSM_CSP_CreateSymmetricContext(refCspHand,
                                           CSSM_ALGID_NONE,
                                           CSSM_ALGMODE_NONE,
                                           &creds,
                                           NULL,			// unwrappingKey
                                           NULL,			// initVector
                                           CSSM_PADDING_NONE,
                                           0,				// Params
                                           &ccHand);
    if(crtn) {
        CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", crtn);
        return crtn;
    }

    keyAttr = rawKey->KeyHeader.KeyAttr;
    keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
                 CSSM_KEYATTR_MODIFIABLE);
    keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
    crtn = CSSM_WrapKey(ccHand,
                        &creds,
                        cssmKey,
                        &descData,
                        rawKey);
    if(crtn != CSSM_OK) {
        CSSM_PERROR("CSSM_WrapKey", crtn);
    }
    CSSM_DeleteContext(ccHand);

loser:
    return crtn;
}
/*
 * 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;
}
/*
 * 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;
}
/*
 * 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;
}
/* 
 * 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 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;
	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;
		}
		signerInfo = SecCmsSignerInfoCreate(cmsEncoder->cmsMsg, ourId, cmsEncoder->digestalgtag);
		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, NULL);
			if(ortn) {
				ortn = cmsRtnToOSStatus(ortn);
				CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
				break;
			}
		}
		if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) {
			ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, NULL);
			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;
			}
		}
        if(cmsEncoder->signedAttributes & kCMSAttrAppleCodesigningHashAgility) {
            ortn = SecCmsSignerInfoAddAppleCodesigningHashAgility(signerInfo, cmsEncoder->hashAgilityAttrValue);
            /* libsecurity_smime made a copy of the attribute value. We don't need it anymore. */
            CFReleaseNull(cmsEncoder->hashAgilityAttrValue);
            if(ortn) {
                ortn = cmsRtnToOSStatus(ortn);
                CSSM_PERROR("SecCmsSignerInfoAddAppleCodesigningHashAgility", ortn);
                break;
            }
        }
		
		ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
		if(ortn) {
			ortn = cmsRtnToOSStatus(ortn);
			CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn);
			break;
		}

		CFRELEASE(ourCert);
		ourCert = NULL;
	}
	if(ortn) {
		CFRELEASE(ourCert);
	}
	return ortn;
}