Exemplo n.º 1
0
/*
 * SecCmsArraySort - sort an array in place
 *
 * If "secondary" or "tertiary are not NULL, it must be arrays with the same
 *  number of elements as "primary". The same reordering will get applied to it.
 *
 * "compare" is a function that returns
 *  < 0 when the first element is less than the second
 *  = 0 when the first element is equal to the second
 *  > 0 when the first element is greater than the second
 * to acheive ascending ordering.
 */
void
SecCmsArraySort(void **primary, int (*compare)(void *,void *), void **secondary, void **tertiary)
{
    int n, i, limit, lastxchg;
    void *tmp;
    int n_2nd=0,n_3rd=0;

    n = SecCmsArrayCount(primary);

    PORT_Assert(secondary == NULL || SecCmsArrayCount(secondary) == n);
    PORT_Assert(tertiary == NULL || SecCmsArrayCount(tertiary) == n);

    if (secondary) {
        n_2nd = SecCmsArrayCount(secondary);
    }
    if (tertiary) {
        n_3rd = SecCmsArrayCount(tertiary);
    }

    if (n <= 1)	/* ordering is fine */
        return;

    /* yes, ladies and gentlemen, it's BUBBLE SORT TIME! */
    limit = n - 1;
    while (1) {
        lastxchg = 0;
        for (i = 0; i < limit; i++) {
            if ((*compare)(primary[i], primary[i+1]) > 0) {
                /* exchange the neighbours */
                tmp = primary[i+1];
                primary[i+1] = primary[i];
                primary[i] = tmp;
                if (secondary && ((i+1)<n_2nd)) {/* secondary array? */
                    tmp = secondary[i+1];	 /* exchange there as well */
                    secondary[i+1] = secondary[i];
                    secondary[i] = tmp;
                }
                if (tertiary && ((i+1)<n_3rd)) {/* tertiary array? */
                    tmp = tertiary[i+1];	/* exchange there as well */
                    tertiary[i+1] = tertiary[i];
                    tertiary[i] = tmp;
                }
                lastxchg = i+1;	/* index of the last element bubbled up */
            }
        }
        if (lastxchg == 0)	/* no exchanges, so array is sorted */
            break;		/* we're done */
        limit = lastxchg;	/* array is sorted up to [limit] */
    }
}
/*
 * SecCmsDigestContextStartMultiple - start digest calculation using all the
 *  digest algorithms in "digestalgs" in parallel.
 */
SecCmsDigestContextRef
SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
{
    SecCmsDigestContextRef cmsdigcx;
    CSSM_CC_HANDLE digobj;
    int digcnt;
    int i;

    digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);

    cmsdigcx = (SecCmsDigestContextRef)PORT_Alloc(sizeof(struct SecCmsDigestContextStr));
    if (cmsdigcx == NULL)
	return NULL;

    if (digcnt > 0) {
	cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_Alloc(digcnt * sizeof(CSSM_CC_HANDLE));
	if (cmsdigcx->digobjs == NULL)
	    goto loser;
    }

    cmsdigcx->digcnt = 0;

    /*
     * Create a digest object context for each algorithm.
     */
    for (i = 0; i < digcnt; i++) {
	digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
	/*
	 * Skip any algorithm we do not even recognize; obviously,
	 * this could be a problem, but if it is critical then the
	 * result will just be that the signature does not verify.
	 * We do not necessarily want to error out here, because
	 * the particular algorithm may not actually be important,
	 * but we cannot know that until later.
	 */
	if (digobj)
        {
            CSSM_RETURN result;
	    result = CSSM_DigestDataInit(digobj);
            if (result != CSSM_OK)
            {
                goto loser;
            }
        }
        
	cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
	cmsdigcx->digcnt++;
    }

    cmsdigcx->saw_contents = PR_FALSE;

    return cmsdigcx;

loser:
    if (cmsdigcx) {
	if (cmsdigcx->digobjs)
	    PORT_Free(cmsdigcx->digobjs);
    }
    return NULL;
}
Exemplo n.º 3
0
/*
 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message
 */
OSStatus
SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, 
                                  SecKeychainRef keychainOrArray, 
                                  CFTypeRef policies)
{
    SecCertificateRef cert;
    OSStatus rv = SECSuccess;
    int i;
    int count;

    if (!sigd || !keychainOrArray || !sigd->rawCerts) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    count = SecCmsArrayCount((void**)sigd->rawCerts);
    for (i=0; i < count; i++) {
	if (sigd->certs && CFArrayGetCount(sigd->certs) > i) {
	    cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i);
	    CFRetain(cert);
	} else {
	    cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]);
	    if (!cert) {
		rv = SECFailure;
		break;
	    }
	}
	rv |= CERT_VerifyCert(keychainOrArray, cert, sigd->rawCerts,
	    policies, CFAbsoluteTimeGetCurrent(), NULL);
	CFRelease(cert);
    }

    return rv;
}
Exemplo n.º 4
0
static void debugSaveCertificates(CSSM_DATA **outCerts)
{
#ifndef NDEBUG
    if (outCerts)
    {
        CSSM_DATA_PTR *certp;
        unsigned jx = 0;
        const char *certNameBase = "/tmp/tsa-resp-cert-";
        char fname[PATH_MAX];
        unsigned certCount = SecCmsArrayCount((void **)outCerts);
        dtprintf("Found %d certs\n",certCount);

        for (certp=outCerts;*certp;certp++, ++jx)
        {
            char numstr[32];
            strncpy(fname, certNameBase, strlen(certNameBase)+1);
            sprintf(numstr,"%u", jx);
            strcat(fname,numstr);
            tsaWriteFileX(fname, (*certp)->Data, (*certp)->Length);
            if (jx > 5)
                break;  //something wrong
        }
    }
#endif
}
Exemplo n.º 5
0
OSStatus
SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd,
				SECOidTag digestalgtag,
				CSSM_DATA_PTR digestdata)
{
    CSSM_DATA_PTR digest = NULL;
    PLArenaPool *poolp;
    void *mark;
    int n, cnt;

    poolp = sigd->cmsg->poolp;

    mark = PORT_ArenaMark(poolp);

   
    if (digestdata) {
        digest = (CSSM_DATA_PTR) PORT_ArenaZAlloc(poolp,sizeof(CSSM_DATA));

	/* copy digestdata item to arena (in case we have it and are not only making room) */
	if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
	    goto loser;
    }

    /* now allocate one (same size as digestAlgorithms) */
    if (sigd->digests == NULL) {
        cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
        sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR));
        if (sigd->digests == NULL) {
	        PORT_SetError(SEC_ERROR_NO_MEMORY);
	        return SECFailure;
        }
    }

    n = -1;
    if (sigd->digestAlgorithms != NULL)
	n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);

    /* if not found, add a digest */
    if (n < 0) {
	if (SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, digest) != SECSuccess)
	    goto loser;
    } else {
	/* replace NULL pointer with digest item (and leak previous value) */
	sigd->digests[n] = digest;
    }

    PORT_ArenaUnmark(poolp, mark);
    return SECSuccess;

loser:
    PORT_ArenaRelease(poolp, mark);
    return SECFailure;
}
Exemplo n.º 6
0
/*
 * SecCmsArraySortByDER - sort array of objects by objects' DER encoding
 *
 * make sure that the order of the objects guarantees valid DER (which must be
 * in lexigraphically ascending order for a SET OF); if reordering is necessary it
 * will be done in place (in objs).
 */
OSStatus
SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2)
{
    PRArenaPool *poolp;
    int num_objs;
    SecAsn1Item **enc_objs;
    OSStatus rv = SECFailure;
    int i;

    if (objs == NULL)					/* already sorted */
	return SECSuccess;

    num_objs = SecCmsArrayCount((void **)objs);
    if (num_objs == 0 || num_objs == 1)		/* already sorted. */
	return SECSuccess;

    poolp = PORT_NewArena (1024);	/* arena for temporaries */
    if (poolp == NULL)
	return SECFailure;		/* no memory; nothing we can do... */

    /*
     * Allocate arrays to hold the individual encodings which we will use
     * for comparisons and the reordered attributes as they are sorted.
     */
    // Security check to prevent under-allocation
    if (num_objs<0 || num_objs>=(int)((INT_MAX/sizeof(SecAsn1Item *))-1)) {
        goto loser;
    }
    enc_objs = (SecAsn1Item **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SecAsn1Item *));
    if (enc_objs == NULL)
	goto loser;

    /* DER encode each individual object. */
    for (i = 0; i < num_objs; i++) {
	enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate);
	if (enc_objs[i] == NULL)
	    goto loser;
    }
    enc_objs[num_objs] = NULL;

    /* now compare and sort objs by the order of enc_objs */
    SecCmsArraySort((void **)enc_objs, SecCmsUtilDERCompare, objs, objs2);

    rv = SECSuccess;

loser:
    PORT_FreeArena (poolp, PR_FALSE);
    return rv;
}
Exemplo n.º 7
0
// Generate a certificate key from the issuer and serialnumber, then look it up in the database.
// Return the cert if found. "issuerAndSN" is the issuer and serial number to look for
SecCertificateRef CERT_FindCertByIssuerAndSN (CFTypeRef keychainOrArray, 
    CSSM_DATA_PTR *rawCerts, PRArenaPool *pl, const SecCmsIssuerAndSN *issuerAndSN)
{
    SecCertificateRef certificate;
    int numRawCerts = SecCmsArrayCount((void **)rawCerts);
    int dex;
    OSStatus ortn;
    
    /* 
     * First search the rawCerts array.
     */
    for(dex=0; dex<numRawCerts; dex++) {
	ortn = SecCertificateCreateFromData(rawCerts[dex], 
	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
	    &certificate);
	if(ortn) {
	    continue;
	}
	SecCmsIssuerAndSN *isn = CERT_GetCertIssuerAndSN(pl, certificate);
	if(isn == NULL) {
	    CFRelease(certificate);
	    continue;
	}
	if(!compareCssmData(&isn->derIssuer, &issuerAndSN->derIssuer)) {
	    CFRelease(certificate);
	    continue;
	}
	if(!compareCssmData(&isn->serialNumber, &issuerAndSN->serialNumber)) {
	    CFRelease(certificate);
	    continue;
	}
	/* got it */
	dprintf("CERT_FindCertByIssuerAndSN: found cert %p\n", certificate);
	return certificate;
    }
    
    /* now search keychain(s) */
    OSStatus status = SecCertificateFindByIssuerAndSN(keychainOrArray, &issuerAndSN->derIssuer,
	&issuerAndSN->serialNumber, &certificate);
    if (status)
    {
	PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
	certificate = NULL;
    }

    return certificate;
}
Exemplo n.º 8
0
/*
 * Return the signing cert of a CMS signerInfo.
 *
 * the certs in the enclosing SignedData must have been imported already
 */
SecCertificateRef
SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
{
    SecCertificateRef cert;
    SecCmsSignerIdentifier *sid;
    OSStatus ortn;
    CSSM_DATA_PTR *rawCerts;
    
    if (signerinfo->cert != NULL) {
	dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
	    signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
	return signerinfo->cert;
    }
    ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
    if(ortn) {
	return NULL;
    }
    dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n", 
	SecCmsArrayCount((void **)rawCerts));
    
    /*
     * This cert will also need to be freed, but since we save it
     * in signerinfo for later, we do not want to destroy it when
     * we leave this function -- we let the clean-up of the entire
     * cinfo structure later do the destroy of this cert.
     */
    sid = &signerinfo->signerIdentifier;
    switch (sid->identifierType) {
    case SecCmsSignerIDIssuerSN:
	cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->cmsg->poolp,
	    sid->id.issuerAndSN);
	break;
    case SecCmsSignerIDSubjectKeyID:
	cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, sid->id.subjectKeyID);
	break;
    default:
	cert = NULL;
	break;
    }

    /* cert can be NULL at that point */
    signerinfo->cert = cert;	/* earmark it */
    dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
	    signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));

    return cert;
}
Exemplo n.º 9
0
/*
 * SecCmsSignedDataSetDigests - set a signedData's digests member
 *
 * "digestalgs" - array of digest algorithm IDs
 * "digests"    - array of digests corresponding to the digest algorithms
 */
OSStatus
SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd,
				SECAlgorithmID **digestalgs,
				CSSM_DATA_PTR *digests)
{
    int cnt, i, idx;

    if (sigd->digestAlgorithms == NULL) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    /* we assume that the digests array is just not there yet */
    PORT_Assert(sigd->digests == NULL);
    if (sigd->digests != NULL) {
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return SECFailure;
    }

    /* now allocate one (same size as digestAlgorithms) */
    cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
    sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR));
    if (sigd->digests == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return SECFailure;
    }

    for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
	/* try to find the sigd's i'th digest algorithm in the array we passed in */
	idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
	if (idx < 0) {
	    PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
	    return SECFailure;
	}

	/* found it - now set it */
	if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
	    SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
	{
	    PORT_SetError(SEC_ERROR_NO_MEMORY);
	    return SECFailure;
	}
    }
    return SECSuccess;
}
Exemplo n.º 10
0
SecCertificateRef CERT_FindCertBySubjectKeyID (CFTypeRef keychainOrArray, 
    CSSM_DATA_PTR *rawCerts, const SECItem *subjKeyID)
{
    SecCertificateRef certificate;
    int numRawCerts = SecCmsArrayCount((void **)rawCerts);
    int dex;
    OSStatus ortn;
    SECItem skid;
    
    /* 
     * First search the rawCerts array.
     */
    for(dex=0; dex<numRawCerts; dex++) {
	int match;
	ortn = SecCertificateCreateFromData(rawCerts[dex], 
	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
	    &certificate);
	if(ortn) {
	    continue;
	}
	if(CERT_FindSubjectKeyIDExtension(certificate, &skid)) {
	    CFRelease(certificate);
	    /* not present */
	    continue;
	}
	match = compareCssmData(subjKeyID, &skid);
	SECITEM_FreeItem(&skid, PR_FALSE);
	if(match) {
	    /* got it */
	    return certificate;
	}
	CFRelease(certificate);
    }

    /* now search keychain(s) */
    OSStatus status = SecCertificateFindBySubjectKeyID(keychainOrArray,subjKeyID,&certificate);
    if (status)
    {
	PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
	certificate = NULL;
    }

    return certificate;
}
Exemplo n.º 11
0
OSStatus
SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain,
				SECCertUsage certusage, Boolean keepcerts)
{
    int certcount;
    OSStatus rv;
    int i;

    certcount = SecCmsArrayCount((void **)sigd->rawCerts);

    rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL,
			  keepcerts, PR_FALSE, NULL);

    /* XXX CRL handling */

    if (sigd->signerInfos != NULL) {
	/* fill in all signerinfo's certs */
	for (i = 0; sigd->signerInfos[i] != NULL; i++)
	    (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[i], keychain);
    }

    return rv;
}
Exemplo n.º 12
0
/*
** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE
** verify a certificate by checking validity times against a certain time,
** that we trust the issuer, and that the signature on the certificate is
** valid.
**	"cert" the certificate to verify
**	"checkSig" only check signatures if true
*/
SECStatus
CERT_VerifyCert(SecKeychainRef keychainOrArray, SecCertificateRef cert,
		const CSSM_DATA_PTR *otherCerts,    /* intermediates */
		CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef)
{
    CFMutableArrayRef certificates = NULL;
    SecTrustRef trust = NULL;
    OSStatus rv;
    int numOtherCerts = SecCmsArrayCount((void **)otherCerts);
    int dex;
    
    /* 
     * Certs to evaluate: first the leaf - our cert - then all the rest we know
     * about. It's OK for otherCerts to contain a copy of the leaf. 
     */
    certificates = CFArrayCreateMutable(NULL, numOtherCerts + 1, &kCFTypeArrayCallBacks);
    CFArrayAppendValue(certificates, cert);
    for(dex=0; dex<numOtherCerts; dex++) {
	SecCertificateRef intCert;
	
	rv = SecCertificateCreateFromData(otherCerts[dex], 
	    CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
	    &intCert);
	if(rv) {
	    goto loser;
	}
	CFArrayAppendValue(certificates, intCert);
	CFRelease(intCert);
    }
    rv = SecTrustCreateWithCertificates(certificates, policies, &trust);
    CFRelease(certificates);
    certificates = NULL;
    if (rv)
	goto loser;

    rv = SecTrustSetKeychains(trust, keychainOrArray);
    if (rv)
	goto loser;

    CFDateRef verifyDate = CFDateCreate(NULL, stime);
    rv = SecTrustSetVerifyDate(trust, verifyDate);
    CFRelease(verifyDate);
    if (rv)
	goto loser;

    if (trustRef)
    {
	*trustRef = trust;
    }
    else
    {
	SecTrustResultType result;
	/* The caller doesn't want a SecTrust object, so let's evaluate it for them. */
	rv = SecTrustEvaluate(trust, &result);
	if (rv)
	    goto loser;

	switch (result)
	{
	case kSecTrustResultProceed:
	case kSecTrustResultUnspecified:
	    /* TP Verification succeeded and there was either a UserTurst entry
	       telling us to procceed, or no user trust setting was specified. */
	    CFRelease(trust);
	    break;
	default:
	    PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
	    rv = SECFailure;
	    goto loser;
	    break;
	}
    }

    return SECSuccess;
loser:
    if (trust)
	CFRelease(trust);
    if(certificates) 
	CFRelease(certificates);
    return rv;
}
Exemplo n.º 13
0
int
SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd)
{
    return SecCmsArrayCount((void **)sigd->signerInfos);
}
Exemplo n.º 14
0
/*
 * SecCmsEnvelopedDataDecodeBeforeData - find our recipientinfo, 
 * derive bulk key & set up our contentinfo
 */
OSStatus
SecCmsEnvelopedDataDecodeBeforeData(SecCmsEnvelopedData *envd)
{
    SecCmsRecipientInfo *ri;
    SecSymmetricKeyRef bulkkey = NULL;
    SECOidTag bulkalgtag;
    SECAlgorithmID *bulkalg;
    OSStatus rv = SECFailure;
    SecCmsContentInfo *cinfo;
    SecCmsRecipient **recipient_list = NULL;
    SecCmsRecipient *recipient;
    int rlIndex;

    if (SecCmsArrayCount((void **)envd->recipientInfos) == 0) {
	PORT_SetError(SEC_ERROR_BAD_DATA);
#if 0
	PORT_SetErrorString("No recipient data in envelope.");
#endif
	goto loser;
    }

    /* look if one of OUR cert's issuerSN is on the list of recipients, and if so,  */
    /* get the cert and private key for it right away */
    recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
    if (recipient_list == NULL)
	goto loser;

    /* what about multiple recipientInfos that match?
     * especially if, for some reason, we could not produce a bulk key with the first match?!
     * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
     * maybe later... */
    rlIndex = nss_cms_FindCertAndKeyByRecipientList(recipient_list, envd->cmsg->pwfn_arg);

    /* if that fails, then we're not an intended recipient and cannot decrypt */
    if (rlIndex < 0) {
	PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
#if 0
	PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
#endif
	goto loser;
    }

    recipient = recipient_list[rlIndex];
    if (!recipient->cert || !recipient->privkey) {
	/* XXX should set an error code ?!? */
	goto loser;
    }
    /* get a pointer to "our" recipientinfo */
    ri = envd->recipientInfos[recipient->riIndex];

    cinfo = &(envd->contentInfo);
    bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo);
    bulkkey = SecCmsRecipientInfoUnwrapBulkKey(ri,recipient->subIndex,
						    recipient->cert,
						    recipient->privkey,
						    bulkalgtag);
    if (bulkkey == NULL) {
	/* no success finding a bulk key */
	goto loser;
    }

    SecCmsContentInfoSetBulkKey(cinfo, bulkkey);
    // @@@ See 3401088 for details.  We need to CFRelease cinfo->bulkkey before recipient->privkey gets CFReleased. It's created with SecKeyCreate which is not safe currently.  If the private key's SecKeyRef from which we extracted the CSP gets CFRelease before the builkkey does we crash.  We should really fix SecKeyCreate which is a huge hack currently.  To work around this we add recipient->privkey to the cinfo so it gets when cinfo is destroyed.
    CFRetain(recipient->privkey);
    cinfo->privkey = recipient->privkey;

    bulkalg = SecCmsContentInfoGetContentEncAlg(cinfo);

    cinfo->ciphcx = SecCmsCipherContextStartDecrypt(bulkkey, bulkalg);
    if (cinfo->ciphcx == NULL)
	goto loser;		/* error has been set by SecCmsCipherContextStartDecrypt */

#if 1
    // @@@ Fix me
#else
    /* 
     * HACK ALERT!!
     * For PKCS5 Encryption Algorithms, the bulkkey is actually a different
     * structure.  Therefore, we need to set the bulkkey to the actual key 
     * prior to freeing it.
     */
    if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
	SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
	bulkkey = keyPwd->key;
    }
#endif

    rv = SECSuccess;

loser:
    if (bulkkey)
	CFRelease(bulkkey);
    if (recipient_list != NULL)
	nss_cms_recipient_list_destroy(recipient_list);
    return rv;
}