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