static void debugShowSignerInfo(SecCmsSignedDataRef signedData) { #ifndef NDEBUG int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData); dtprintf("numberOfSigners : %d\n", numberOfSigners); int ix; for (ix=0;ix < numberOfSigners;ix++) { SecCmsSignerInfoRef sigi = SecCmsSignedDataGetSignerInfo(signedData,ix); if (sigi) { CFStringRef commonName = SecCmsSignerInfoGetSignerCommonName(sigi); const char *signerhdr = " signer : "; if (commonName) { char *cn = cfStringToChar(commonName); dtprintf("%s%s\n", signerhdr, cn); if (cn) free(cn); CFReleaseNull(commonName); } else dtprintf("%s<NULL>\n", signerhdr); } } #endif }
/* * 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; }
void Download::Initialize (CFDataRef ticket, SecureDownloadTrustSetupCallback setup, void* setupContext, SecureDownloadTrustEvaluateCallback evaluate, void* evaluateContext) { // decode the ticket SecCmsMessageRef cmsMessage = GetCmsMessageFromData (ticket); // get a policy SecPolicyRef policy = GetPolicy (); // parse the CMS message int contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage); SecCmsSignedDataRef signedData; OSStatus result; int i = 0; while (i < contentLevelCount) { SecCmsContentInfoRef contentInfo = SecCmsMessageContentLevel (cmsMessage, i++); SECOidTag contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo); if (contentTypeTag != SEC_OID_PKCS7_SIGNED_DATA) { continue; } signedData = (SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo); if (signedData == NULL) { MacOSError::throwMe (errSecureDownloadInvalidTicket); } // import the certificates found in the cms message result = SecCmsSignedDataImportCerts (signedData, NULL, certUsageObjectSigner, true); if (result != 0) { MacOSError::throwMe (errSecureDownloadInvalidTicket); } int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData); int j; if (numberOfSigners == 0) // no signers? This is a possible attack { MacOSError::throwMe (errSecureDownloadInvalidTicket); } for (j = 0; j < numberOfSigners; ++j) { SecTrustResultType resultType; // do basic verification of the message SecTrustRef trustRef; result = SecCmsSignedDataVerifySignerInfo (signedData, j, NULL, policy, &trustRef); // notify the user of the new trust ref if (setup != NULL) { SecureDownloadTrustCallbackResult tcResult = setup (trustRef, setupContext); switch (tcResult) { case kSecureDownloadDoNotEvaluateSigner: continue; case kSecureDownloadFailEvaluation: MacOSError::throwMe (errSecureDownloadInvalidTicket); case kSecureDownloadEvaluateSigner: break; } } if (result != 0) { MacOSError::throwMe (errSecureDownloadInvalidTicket); } result = SecTrustEvaluate (trustRef, &resultType); if (result != noErr) { MacOSError::throwMe (errSecureDownloadInvalidTicket); } if (evaluate != NULL) { resultType = evaluate (trustRef, resultType, evaluateContext); } GoOrNoGo (resultType); } } // extract the message CSSM_DATA_PTR message = SecCmsMessageGetContent (cmsMessage); CFDataRef ticketData = CFDataCreateWithBytesNoCopy (NULL, message->Data, message->Length, kCFAllocatorNull); CheckCFThingForNULL (ticketData); ParseTicket (ticketData); // setup for hashing CC_SHA256_Init (&mSHA256Context); // clean up CFRelease (ticketData); SecCmsMessageDestroy (cmsMessage); }
/* * 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; }
/* * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData * before start of encoding. * * In detail: * - find out about the right value to put into sigd->version * - come up with a list of digestAlgorithms (which should be the union of the algorithms * in the signerinfos). * If we happen to have a pre-set list of algorithms (and digest values!), we * check if we have all the signerinfos' algorithms. If not, this is an error. */ OSStatus SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd) { SecCmsSignerInfoRef signerinfo; SECOidTag digestalgtag; CSSM_DATA_PTR dummy; int version; OSStatus rv; Boolean haveDigests = PR_FALSE; int n, i; PLArenaPool *poolp; poolp = sigd->cmsg->poolp; /* we assume that we have precomputed digests if there is a list of algorithms, and */ /* a chunk of data for each of those algorithms */ if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) { for (i=0; sigd->digestAlgorithms[i] != NULL; i++) { if (sigd->digests[i] == NULL) break; } if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */ haveDigests = PR_TRUE; /* yes: we must have all the digests */ } version = SEC_CMS_SIGNED_DATA_VERSION_BASIC; /* RFC2630 5.1 "version is the syntax version number..." */ if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA) version = SEC_CMS_SIGNED_DATA_VERSION_EXT; /* prepare all the SignerInfos (there may be none) */ for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); /* RFC2630 5.1 "version is the syntax version number..." */ if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN) version = SEC_CMS_SIGNED_DATA_VERSION_EXT; /* collect digestAlgorithms from SignerInfos */ /* (we need to know which algorithms we have when the content comes in) */ /* do not overwrite any existing digestAlgorithms (and digest) */ digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); if (n < 0 && haveDigests) { /* oops, there is a digestalg we do not have a digest for */ /* but we were supposed to have all the digests already... */ goto loser; } else if (n < 0) { /* add the digestAlgorithm & a NULL digest */ rv = SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, NULL); if (rv != SECSuccess) goto loser; } else { /* found it, nothing to do */ } } dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version); if (dummy == NULL) return SECFailure; /* this is a SET OF, so we need to sort them guys */ rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), (void **)sigd->digests); if (rv != SECSuccess) return SECFailure; return SECSuccess; loser: return SECFailure; }