Пример #1
0
static SECStatus
smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
{
    PRBool isFortezza = PR_FALSE;

    PORT_Assert (smime_prefs_complete);
    if (! smime_prefs_complete)
	return SECFailure;

    /* See if the sender's cert specifies Fortezza key exchange. */
    if (cert != NULL)
	isFortezza = PK11_FortezzaHasKEA(cert);

    /* For that matter, if capabilities haven't been initialized yet,
       do so now. */
    if (isFortezza != lastUsedFortezza || smime_encoded_caps == NULL || smime_prefs_changed) {
	SECStatus rv;

	rv = smime_init_caps(isFortezza);
	if (rv != SECSuccess)
	    return rv;

	PORT_Assert (smime_encoded_caps != NULL);
    }

    return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
					smime_encoded_caps);
}
Пример #2
0
/* 
 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
 * authenticated (i.e. signed) attributes of "signerinfo". 
 *
 * This is expected to be included in outgoing signed
 * messages for email (S/MIME).
 */
OSStatus
SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
{
    SecCmsAttribute *attr;
    CSSM_DATA_PTR smimecaps = NULL;
    void *mark;
    PLArenaPool *poolp;

    poolp = signerinfo->cmsg->poolp;

    mark = PORT_ArenaMark(poolp);

    smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
    if (smimecaps == NULL)
	goto loser;

    /* create new signing time attribute */
#if 1
    // @@@ We don't do Fortezza yet.
    if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
#else
    if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
			    PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
#endif
	goto loser;

    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
	goto loser;

    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
	goto loser;

    PORT_ArenaUnmark (poolp, mark);
    return SECSuccess;

loser:
    PORT_ArenaRelease (poolp, mark);
    return SECFailure;
}
Пример #3
0
/*
 * smime_choose_cipher - choose a cipher that works for all the recipients
 *
 * "scert"  - sender's certificate
 * "rcerts" - recipient's certificates
 */
static long
smime_choose_cipher(SecCertificateRef scert, SecCertificateRef *rcerts)
{
    PRArenaPool *poolp;
    long cipher;
    long chosen_cipher;
    int *cipher_abilities;
    int *cipher_votes;
    int weak_mapi;
    int strong_mapi;
    int rcount, mapi, max, i;
#if 1
    // @@@ We Don't support Fortezza yet.
    Boolean scert_is_fortezza  = PR_FALSE;
#else
    Boolean scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
#endif

    chosen_cipher = SMIME_RC2_CBC_40;		/* the default, LCD */
    weak_mapi = smime_mapi_by_cipher(chosen_cipher);

    poolp = PORT_NewArena (1024);		/* XXX what is right value? */
    if (poolp == NULL)
	goto done;

    cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
    cipher_votes     = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
    if (cipher_votes == NULL || cipher_abilities == NULL)
	goto done;

    /* If the user has the Fortezza preference turned on, make
     *  that the strong cipher. Otherwise, use triple-DES. */
    strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
    if (scert_is_fortezza) {
	mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
	if (mapi >= 0 && smime_cipher_map[mapi].enabled)
	    strong_mapi = mapi;
    }

    /* walk all the recipient's certs */
    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
	CSSM_DATA_PTR profile;
	NSSSMIMECapability **caps;
	int pref;

	/* the first cipher that matches in the user's SMIME profile gets
	 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
	 * and so on. If every cipher matches, the last one gets 1 (one) vote */
	pref = smime_cipher_map_count;

	/* find recipient's SMIME profile */
	profile = CERT_FindSMimeProfile(rcerts[rcount]);

	if (profile != NULL && profile->Data != NULL && profile->Length > 0) {
	    /* we have a profile (still DER-encoded) */
	    caps = NULL;
	    /* decode it */
	    if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
		    caps != NULL)
	    {
		/* walk the SMIME capabilities for this recipient */
		for (i = 0; caps[i] != NULL; i++) {
		    cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
		    mapi = smime_mapi_by_cipher(cipher);
		    if (mapi >= 0) {
			/* found the cipher */
			cipher_abilities[mapi]++;
			cipher_votes[mapi] += pref;
			--pref;
		    }
		}
	    }
	} else {
	    /* no profile found - so we can only assume that the user can do
	     * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
	    SecPublicKeyRef key;
	    unsigned int pklen_bits;

	    /*
	     * if recipient's public key length is > 512, vote for a strong cipher
	     * please not that the side effect of this is that if only one recipient
	     * has an export-level public key, the strong cipher is disabled.
	     *
	     * XXX This is probably only good for RSA keys.  What I would
	     * really like is a function to just say;  Is the public key in
	     * this cert an export-length key?  Then I would not have to
	     * know things like the value 512, or the kind of key, or what
	     * a subjectPublicKeyInfo is, etc.
	     */
	    key = CERT_ExtractPublicKey(rcerts[rcount]);
	    pklen_bits = 0;
	    if (key != NULL) {
		SecKeyGetStrengthInBits(key, NULL, &pklen_bits);
		SECKEY_DestroyPublicKey (key);
	    }

	    if (pklen_bits > 512) {
		/* cast votes for the strong algorithm */
		cipher_abilities[strong_mapi]++;
		cipher_votes[strong_mapi] += pref;
		pref--;
	    } 

	    /* always cast (possibly less) votes for the weak algorithm */
	    cipher_abilities[weak_mapi]++;
	    cipher_votes[weak_mapi] += pref;
	}
	if (profile != NULL)
	    SECITEM_FreeItem(profile, PR_TRUE);
    }

    /* find cipher that is agreeable by all recipients and that has the most votes */
    max = 0;
    for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
	/* if not all of the recipients can do this, forget it */
	if (cipher_abilities[mapi] != rcount)
	    continue;
	/* if cipher is not enabled or not allowed by policy, forget it */
	if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
	    continue;
	/* if we're not doing fortezza, but the cipher is fortezza, forget it */
	if (!scert_is_fortezza  && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
	    continue;
	/* now see if this one has more votes than the last best one */
	if (cipher_votes[mapi] >= max) {
	    /* if equal number of votes, prefer the ones further down in the list */
	    /* with the expectation that these are higher rated ciphers */
	    chosen_cipher = smime_cipher_map[mapi].cipher;
	    max = cipher_votes[mapi];
	}
    }
    /* if no common cipher was found, chosen_cipher stays at the default */

done:
    if (poolp != NULL)
	PORT_FreeArena (poolp, PR_FALSE);

    if (smime_keysize_by_cipher(chosen_cipher) < 128) {
        /* you're going to use strong(er) crypto whether you like it or not */
        chosen_cipher = SMIME_DES_EDE3_168;
    }
    return chosen_cipher;
}
Пример #4
0
static long
smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts)
{
    PRArenaPool *poolp;
    long chosen_cipher;
    int *cipher_abilities;
    int *cipher_votes;
    int strong_mapi;
    int rcount, mapi, max, i;
	PRBool isFortezza = PK11_FortezzaHasKEA(scert);

    if (smime_policy_bits == 0) {
	PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
	return -1;
    }

    chosen_cipher = SMIME_RC2_CBC_40;		/* the default, LCD */

    poolp = PORT_NewArena (1024);		/* XXX what is right value? */
    if (poolp == NULL)
	goto done;

    cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,
					 smime_symmetric_count * sizeof(int));
    if (cipher_abilities == NULL)
	goto done;

    cipher_votes = (int*)PORT_ArenaZAlloc (poolp,
				     smime_symmetric_count * sizeof(int));
    if (cipher_votes == NULL)
	goto done;

    /*
     * XXX Should have a #define somewhere which specifies default
     * strong cipher.  (Or better, a way to configure, which would
     * take Fortezza into account as well.)
     */

    /* If the user has the Fortezza preference turned on, make
     *  that the strong cipher. Otherwise, use triple-DES. */
    strong_mapi = -1;
    if (isFortezza) {
	for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++)
	{
	    if (smime_prefs[i] == SMIME_FORTEZZA)
		strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
	}
    }

    if (strong_mapi == -1)
	strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);

    PORT_Assert (strong_mapi >= 0);

    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
	SECItem *profile;
	smime_capability **caps;
	int capi, pref;
	SECStatus dstat;

	pref = smime_symmetric_count;
	profile = CERT_FindSMimeProfile (rcerts[rcount]);
	if (profile != NULL && profile->data != NULL && profile->len > 0) {
	    caps = NULL;
	    dstat = SEC_QuickDERDecodeItem (poolp, &caps,
					smime_capabilities_template,
					profile);
	    if (dstat == SECSuccess && caps != NULL) {
		for (capi = 0; caps[capi] != NULL; capi++) {
		    smime_fill_capability (caps[capi]);
		    mapi = smime_mapi_by_cipher (caps[capi]->cipher);
		    if (mapi >= 0) {
			cipher_abilities[mapi]++;
			cipher_votes[mapi] += pref;
			--pref;
		    }
		}
	    }
	} else {
	    SECKEYPublicKey *key;
	    unsigned int pklen_bits;

	    /*
	     * XXX This is probably only good for RSA keys.  What I would
	     * really like is a function to just say;  Is the public key in
	     * this cert an export-length key?  Then I would not have to
	     * know things like the value 512, or the kind of key, or what
	     * a subjectPublicKeyInfo is, etc.
	     */
	    key = CERT_ExtractPublicKey (rcerts[rcount]);
	    if (key != NULL) {
		pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
		SECKEY_DestroyPublicKey (key);

		if (pklen_bits > 512) {
		    cipher_abilities[strong_mapi]++;
		    cipher_votes[strong_mapi] += pref;
		}
	    }
	}
	if (profile != NULL)
	    SECITEM_FreeItem (profile, PR_TRUE);
    }

    max = 0;
    for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
	if (cipher_abilities[mapi] != rcount)
	    continue;
	if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher))
	    continue;
	if (!isFortezza  && (smime_cipher_maps[mapi].cipher == SMIME_FORTEZZA))
		continue;
	if (cipher_votes[mapi] > max) {
	    chosen_cipher = smime_cipher_maps[mapi].cipher;
	    max = cipher_votes[mapi];
	} /* XXX else if a tie, let scert break it? */
    }

done:
    if (poolp != NULL)
	PORT_FreeArena (poolp, PR_FALSE);

    return chosen_cipher;
}