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; }
/* * 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; }