/* * 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; }
/* * SecCmsSignedDataVerifySignerInfo - check the signatures. * * The digests were either calculated during decoding (and are stored in the * signedData itself) or set after decoding using SecCmsSignedDataSetDigests. * * The verification checks if the signing cert is valid and has a trusted chain * for the purpose specified by "policies". * * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly. * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate(). */ OSStatus SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i, SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef) { SecCmsSignerInfoRef signerinfo; SecCmsContentInfoRef cinfo; SECOidData *algiddata; CSSM_DATA_PTR contentType, digest; OSStatus status, status2; cinfo = &(sigd->contentInfo); signerinfo = sigd->signerInfos[i]; /* Signature or digest level verificationStatus errors should supercede certificate level errors, so check the digest and signature first. */ /* Find digest and contentType for signerinfo */ algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo); if (algiddata == NULL) { return errSecInternalError; // shouldn't have happened, this is likely due to corrupted data } digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset); if(digest == NULL) { /* * No digests; this probably had detached content the caller has to * deal with. * FIXME: need some error return for this (as well as many * other places in this library). */ return errSecDataNotAvailable; } contentType = SecCmsContentInfoGetContentTypeOID(cinfo); /* verify signature */ CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); status = SecCmsSignerInfoVerifyWithPolicy(signerinfo, timeStampPolicies, digest, contentType); CFReleaseSafe(timeStampPolicies); /* Now verify the certificate. We do this even if the signature failed to verify so we can return a trustRef to the caller for display purposes. */ status2 = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray, policies, trustRef); dprintf("SecCmsSignedDataVerifySignerInfo: status %d status2 %d\n", (int) status, (int)status2); /* The error from SecCmsSignerInfoVerify() supercedes error from SecCmsSignerInfoVerifyCertificate(). */ if (status) return status; return status2; }
static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo) { #ifndef NDEBUG CSSM_OID *typeOID = SecCmsContentInfoGetContentTypeOID(contentInfo); if (typeOID) { CFStringRef oidCFStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, typeOID); char *oidstr = cfStringToChar(oidCFStr); printDataAsHex("oid:", typeOID, (unsigned int)typeOID->Length); dtprintf("\toid: %s\n", oidstr); if (oidCFStr) CFRelease(oidCFStr); if (oidstr) free(oidstr); } #endif }
/* * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData * after all the encapsulated data was passed through the encoder. * * In detail: * - create the signatures in all the SignerInfos * * Please note that nothing is done to the Certificates and CRLs in the message - this * is entirely the responsibility of our callers. */ OSStatus SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd) { SecCmsSignerInfoRef *signerinfos, signerinfo; SecCmsContentInfoRef cinfo; SECOidTag digestalgtag; OSStatus ret = SECFailure; OSStatus rv; CSSM_DATA_PTR contentType; int certcount; int i, ci, n, rci, si; PLArenaPool *poolp; CFArrayRef certlist; extern const SecAsn1Template SecCmsSignerInfoTemplate[]; poolp = sigd->cmsg->poolp; cinfo = &(sigd->contentInfo); /* did we have digest calculation going on? */ if (cinfo->digcx) { rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests)); if (rv != SECSuccess) goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ cinfo->digcx = NULL; } signerinfos = sigd->signerInfos; certcount = 0; /* prepare all the SignerInfos (there may be none) */ for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); /* find correct digest for this signerinfo */ digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) { /* oops - digest not found */ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); goto loser; } /* XXX if our content is anything else but data, we need to force the * presence of signed attributes (RFC2630 5.3 "signedAttributes is a * collection...") */ /* pass contentType here as we want a contentType attribute */ if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL) goto loser; /* sign the thing */ rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType); if (rv != SECSuccess) goto loser; /* while we're at it, count number of certs in certLists */ certlist = SecCmsSignerInfoGetCertList(signerinfo); if (certlist) certcount += CFArrayGetCount(certlist); } /* Now we can get a timestamp, since we have all the digests */ // We force the setting of a callback, since this is the most usual case if (!sigd->cmsg->tsaCallback) SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback); if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext) { CSSM_DATA tsaResponse = {0,}; SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}}; // <rdar://problem/11073466> Add nonce support for timestamping client uint64_t nonce = 0; require_noerr(getRandomNonce(&nonce), tsxit); dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd)); // Calculate hash of encDigest and put in messageImprint.hashedMessage SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0); // NB - assume 1 signer only! CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo); require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit); // Callback to fire up XPC service to talk to TimeStamping server, etc. require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint, nonce, &tsaResponse), tsxit); require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit); /* It is likely that every occurrence of "goto loser" in this file should also do a PORT_SetError. Since it is not clear what might depend on this behavior, we just do this in the timestamping case. */ tsxit: if (rv) { dprintf("Original timestamp error: %d\n", (int)rv); rv = remapTimestampError(rv); PORT_SetError(rv); goto loser; } } /* this is a SET OF, so we need to sort them guys */ rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL); if (rv != SECSuccess) goto loser; /* * now prepare certs & crls */ /* count the rest of the certs */ if (sigd->certs != NULL) certcount += CFArrayGetCount(sigd->certs); if (certcount == 0) { sigd->rawCerts = NULL; } else { /* * Combine all of the certs and cert chains into rawcerts. * Note: certcount is an upper bound; we may not need that many slots * but we will allocate anyway to avoid having to do another pass. * (The temporary space saving is not worth it.) * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR)); if (sigd->rawCerts == NULL) return SECFailure; /* * XXX Want to check for duplicates and not add *any* cert that is * already in the set. This will be more important when we start * dealing with larger sets of certs, dual-key certs (signing and * encryption), etc. For the time being we can slide by... * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ rci = 0; if (signerinfos != NULL) { for (si = 0; signerinfos[si] != NULL; si++) { signerinfo = signerinfos[si]; for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } } if (sigd->certs != NULL) { for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } sigd->rawCerts[rci] = NULL; /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); } ret = SECSuccess; loser: dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv); return ret; }