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