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;
}
Example #2
0
/*
 * Create a CMS message: either encrypted (EnvelopedData), signed 
 * (SignedData), or both (EnvelopedData(SignedData(content)).
 *
 * The message is signed iff signing_cert is non-NULL.
 * The message is encrypted iff recip_cert is non-NULL.
 *
 * The content_type argument specifies to the eContentType
 * for a SignedData's EncapsulatedContentInfo. 
 */
krb5_error_code krb5int_pkinit_create_cms_msg(
    const krb5_data		*content,	/* Content */
    krb5_pkinit_signing_cert_t	signing_cert,	/* optional: signed by this cert */
    const krb5_data		*recip_cert,	/* optional: encrypted with this cert */
    krb5int_cms_content_type	content_type,   /* OID for EncapsulatedData */
    krb5_ui_4			num_cms_types,	/* optional, unused here */
    const krb5int_algorithm_id	*cms_types,	/* optional, unused here */
    krb5_data			*content_info)  /* contents mallocd and RETURNED */
{
    krb5_error_code krtn;
    OSStatus ortn;
    SecCertificateRef sec_recip = NULL;
    CFDataRef cf_content = NULL;
    const CSSM_OID *eContentOid = NULL;
    
    if((signing_cert == NULL) && (recip_cert == NULL)) {
	/* must have one or the other */
	pkiDebug("krb5int_pkinit_create_cms_msg: no signer or recipient\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    /* 
     * Optional signer cert. Note signing_cert, if present, is 
     * a SecIdentityRef. 
     */
    if(recip_cert) {
	if(pkiKrb5DataToSecCert(recip_cert, &sec_recip)) {
	    krtn = ASN1_BAD_FORMAT;
	    goto errOut;
	}
    }
    
    /* optional eContentType */
    if(signing_cert) {
	switch(content_type) {
	    case ECT_PkAuthData:
		eContentOid = &_CSSMOID_PKINIT_AUTH_DATA;
		break;
	    case ECT_PkReplyKeyKata:
		eContentOid = &_CSSMOID_PKINIT_RKEY_DATA;
		break;
	    case ECT_Data:
		/* the only standard/default case we allow */
		break;
	    default:
		/* others: no can do */
		pkiDebug("krb5int_pkinit_create_cms_msg: bad contentType\n");
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	}
    }
    
    /* GO */
    ortn = CMSEncode((SecIdentityRef)signing_cert, sec_recip,
	eContentOid, 
	FALSE,		/* detachedContent */
	kCMSAttrNone,	/* no signed attributes that I know of */
	content->data, content->length,
	&cf_content);
    if(ortn) {
	pkiCssmErr("CMSEncode", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    krtn = pkiCfDataToKrb5Data(cf_content, content_info);
errOut:
    CFRELEASE(sec_recip);
    CFRELEASE(cf_content);
    return krtn;
}
/*
 * 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;
}