security/nss/lib/smime/smimeutil.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/smime/smimeutil.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,781 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * Stuff specific to S/MIME policy and interoperability.
    1.10 + */
    1.11 +
    1.12 +#include "secmime.h"
    1.13 +#include "secoid.h"
    1.14 +#include "pk11func.h"
    1.15 +#include "ciferfam.h"	/* for CIPHER_FAMILY symbols */
    1.16 +#include "secasn1.h"
    1.17 +#include "secitem.h"
    1.18 +#include "cert.h"
    1.19 +#include "key.h"
    1.20 +#include "secerr.h"
    1.21 +#include "cms.h"
    1.22 +#include "nss.h"
    1.23 +
    1.24 +SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
    1.25 +SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
    1.26 +SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
    1.27 +
    1.28 +/* various integer's ASN.1 encoding */
    1.29 +static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
    1.30 +static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
    1.31 +static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
    1.32 +
    1.33 +/* RC2 algorithm parameters (used in smime_cipher_map) */
    1.34 +static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
    1.35 +static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
    1.36 +static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
    1.37 +
    1.38 +/*
    1.39 + * XXX Would like the "parameters" field to be a SECItem *, but the
    1.40 + * encoder is having trouble with optional pointers to an ANY.  Maybe
    1.41 + * once that is fixed, can change this back...
    1.42 + */
    1.43 +typedef struct {
    1.44 +    SECItem capabilityID;
    1.45 +    SECItem parameters;
    1.46 +    long cipher;		/* optimization */
    1.47 +} NSSSMIMECapability;
    1.48 +
    1.49 +static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
    1.50 +    { SEC_ASN1_SEQUENCE,
    1.51 +	  0, NULL, sizeof(NSSSMIMECapability) },
    1.52 +    { SEC_ASN1_OBJECT_ID,
    1.53 +	  offsetof(NSSSMIMECapability,capabilityID), },
    1.54 +    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
    1.55 +	  offsetof(NSSSMIMECapability,parameters), },
    1.56 +    { 0, }
    1.57 +};
    1.58 +
    1.59 +static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
    1.60 +    { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
    1.61 +};
    1.62 +
    1.63 +/*
    1.64 + * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
    1.65 + *  to store this and only this certificate permanently for the sender email address.
    1.66 + */
    1.67 +typedef enum {
    1.68 +    NSSSMIMEEncryptionKeyPref_IssuerSN,
    1.69 +    NSSSMIMEEncryptionKeyPref_RKeyID,
    1.70 +    NSSSMIMEEncryptionKeyPref_SubjectKeyID
    1.71 +} NSSSMIMEEncryptionKeyPrefSelector;
    1.72 +
    1.73 +typedef struct {
    1.74 +    NSSSMIMEEncryptionKeyPrefSelector selector;
    1.75 +    union {
    1.76 +	CERTIssuerAndSN			*issuerAndSN;
    1.77 +	NSSCMSRecipientKeyIdentifier	*recipientKeyID;
    1.78 +	SECItem				*subjectKeyID;
    1.79 +    } id;
    1.80 +} NSSSMIMEEncryptionKeyPreference;
    1.81 +
    1.82 +extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
    1.83 +
    1.84 +static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
    1.85 +    { SEC_ASN1_CHOICE,
    1.86 +	  offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
    1.87 +	  sizeof(NSSSMIMEEncryptionKeyPreference) },
    1.88 +    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0
    1.89 +          | SEC_ASN1_CONSTRUCTED,
    1.90 +	  offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
    1.91 +	  SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
    1.92 +	  NSSSMIMEEncryptionKeyPref_IssuerSN },
    1.93 +    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1
    1.94 +          | SEC_ASN1_CONSTRUCTED,
    1.95 +	  offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
    1.96 +	  NSSCMSRecipientKeyIdentifierTemplate,
    1.97 +	  NSSSMIMEEncryptionKeyPref_RKeyID },
    1.98 +    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2
    1.99 +          | SEC_ASN1_CONSTRUCTED,
   1.100 +	  offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
   1.101 +	  SEC_ASN1_SUB(SEC_OctetStringTemplate),
   1.102 +	  NSSSMIMEEncryptionKeyPref_SubjectKeyID },
   1.103 +    { 0, }
   1.104 +};
   1.105 +
   1.106 +/* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
   1.107 +typedef struct {
   1.108 +    unsigned long cipher;
   1.109 +    SECOidTag algtag;
   1.110 +    SECItem *parms;
   1.111 +    PRBool enabled;	/* in the user's preferences */
   1.112 +    PRBool allowed;	/* per export policy */
   1.113 +} smime_cipher_map_entry;
   1.114 +
   1.115 +/* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
   1.116 +static smime_cipher_map_entry smime_cipher_map[] = {
   1.117 +/*    cipher			algtag			parms		enabled  allowed */
   1.118 +/*    ---------------------------------------------------------------------------------- */
   1.119 +    { SMIME_RC2_CBC_40,		SEC_OID_RC2_CBC,	&param_int40,	PR_TRUE, PR_TRUE },
   1.120 +    { SMIME_DES_CBC_56,		SEC_OID_DES_CBC,	NULL,		PR_TRUE, PR_TRUE },
   1.121 +    { SMIME_RC2_CBC_64,		SEC_OID_RC2_CBC,	&param_int64,	PR_TRUE, PR_TRUE },
   1.122 +    { SMIME_RC2_CBC_128,	SEC_OID_RC2_CBC,	&param_int128,	PR_TRUE, PR_TRUE },
   1.123 +    { SMIME_DES_EDE3_168,	SEC_OID_DES_EDE3_CBC,	NULL,		PR_TRUE, PR_TRUE },
   1.124 +    { SMIME_AES_CBC_128,	SEC_OID_AES_128_CBC,	NULL,		PR_TRUE, PR_TRUE },
   1.125 +    { SMIME_AES_CBC_256,	SEC_OID_AES_256_CBC,	NULL,		PR_TRUE, PR_TRUE }
   1.126 +};
   1.127 +static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
   1.128 +
   1.129 +/*
   1.130 + * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
   1.131 + */
   1.132 +static int
   1.133 +smime_mapi_by_cipher(unsigned long cipher)
   1.134 +{
   1.135 +    int i;
   1.136 +
   1.137 +    for (i = 0; i < smime_cipher_map_count; i++) {
   1.138 +	if (smime_cipher_map[i].cipher == cipher)
   1.139 +	    return i;	/* bingo */
   1.140 +    }
   1.141 +    return -1;		/* should not happen if we're consistent, right? */
   1.142 +}
   1.143 +
   1.144 +/*
   1.145 + * NSS_SMIME_EnableCipher - this function locally records the user's preference
   1.146 + */
   1.147 +SECStatus 
   1.148 +NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
   1.149 +{
   1.150 +    unsigned long mask;
   1.151 +    int mapi;
   1.152 +
   1.153 +    mask = which & CIPHER_FAMILYID_MASK;
   1.154 +
   1.155 +    PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
   1.156 +    if (mask != CIPHER_FAMILYID_SMIME)
   1.157 +	/* XXX set an error! */
   1.158 +    	return SECFailure;
   1.159 +
   1.160 +    mapi = smime_mapi_by_cipher(which);
   1.161 +    if (mapi < 0)
   1.162 +	/* XXX set an error */
   1.163 +	return SECFailure;
   1.164 +
   1.165 +    /* do we try to turn on a forbidden cipher? */
   1.166 +    if (!smime_cipher_map[mapi].allowed && on) {
   1.167 +	PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
   1.168 +	return SECFailure;
   1.169 +    }
   1.170 +
   1.171 +    if (smime_cipher_map[mapi].enabled != on)
   1.172 +	smime_cipher_map[mapi].enabled = on;
   1.173 +
   1.174 +    return SECSuccess;
   1.175 +}
   1.176 +
   1.177 +
   1.178 +/*
   1.179 + * this function locally records the export policy
   1.180 + */
   1.181 +SECStatus 
   1.182 +NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
   1.183 +{
   1.184 +    unsigned long mask;
   1.185 +    int mapi;
   1.186 +
   1.187 +    mask = which & CIPHER_FAMILYID_MASK;
   1.188 +
   1.189 +    PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
   1.190 +    if (mask != CIPHER_FAMILYID_SMIME)
   1.191 +	/* XXX set an error! */
   1.192 +    	return SECFailure;
   1.193 +
   1.194 +    mapi = smime_mapi_by_cipher(which);
   1.195 +    if (mapi < 0)
   1.196 +	/* XXX set an error */
   1.197 +	return SECFailure;
   1.198 +
   1.199 +    if (smime_cipher_map[mapi].allowed != on)
   1.200 +	smime_cipher_map[mapi].allowed = on;
   1.201 +
   1.202 +    return SECSuccess;
   1.203 +}
   1.204 +
   1.205 +/*
   1.206 + * Based on the given algorithm (including its parameters, in some cases!)
   1.207 + * and the given key (may or may not be inspected, depending on the
   1.208 + * algorithm), find the appropriate policy algorithm specification
   1.209 + * and return it.  If no match can be made, -1 is returned.
   1.210 + */
   1.211 +static SECStatus
   1.212 +nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
   1.213 +{
   1.214 +    SECOidTag algtag;
   1.215 +    unsigned int keylen_bits;
   1.216 +    unsigned long c;
   1.217 +
   1.218 +    algtag = SECOID_GetAlgorithmTag(algid);
   1.219 +    switch (algtag) {
   1.220 +    case SEC_OID_RC2_CBC:
   1.221 +	keylen_bits = PK11_GetKeyStrength(key, algid);
   1.222 +	switch (keylen_bits) {
   1.223 +	case 40:
   1.224 +	    c = SMIME_RC2_CBC_40;
   1.225 +	    break;
   1.226 +	case 64:
   1.227 +	    c = SMIME_RC2_CBC_64;
   1.228 +	    break;
   1.229 +	case 128:
   1.230 +	    c = SMIME_RC2_CBC_128;
   1.231 +	    break;
   1.232 +	default:
   1.233 +	    return SECFailure;
   1.234 +	}
   1.235 +	break;
   1.236 +    case SEC_OID_DES_CBC:
   1.237 +	c = SMIME_DES_CBC_56;
   1.238 +	break;
   1.239 +    case SEC_OID_DES_EDE3_CBC:
   1.240 +	c = SMIME_DES_EDE3_168;
   1.241 +	break;
   1.242 +    case SEC_OID_AES_128_CBC:
   1.243 +	c = SMIME_AES_CBC_128;
   1.244 +	break;
   1.245 +    case SEC_OID_AES_256_CBC:
   1.246 +	c = SMIME_AES_CBC_256;
   1.247 +	break;
   1.248 +    default:
   1.249 +	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1.250 +	return SECFailure;
   1.251 +    }
   1.252 +    *cipher = c;
   1.253 +    return SECSuccess;
   1.254 +}
   1.255 +
   1.256 +static PRBool
   1.257 +nss_smime_cipher_allowed(unsigned long which)
   1.258 +{
   1.259 +    int mapi;
   1.260 +
   1.261 +    mapi = smime_mapi_by_cipher(which);
   1.262 +    if (mapi < 0)
   1.263 +	return PR_FALSE;
   1.264 +    return smime_cipher_map[mapi].allowed;
   1.265 +}
   1.266 +
   1.267 +PRBool
   1.268 +NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
   1.269 +{
   1.270 +    unsigned long which;
   1.271 +
   1.272 +    if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
   1.273 +	return PR_FALSE;
   1.274 +
   1.275 +    return nss_smime_cipher_allowed(which);
   1.276 +}
   1.277 +
   1.278 +
   1.279 +/*
   1.280 + * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
   1.281 + *
   1.282 + * This tells whether or not *any* S/MIME encryption can be done,
   1.283 + * according to policy.  Callers may use this to do nicer user interface
   1.284 + * (say, greying out a checkbox so a user does not even try to encrypt
   1.285 + * a message when they are not allowed to) or for any reason they want
   1.286 + * to check whether S/MIME encryption (or decryption, for that matter)
   1.287 + * may be done.
   1.288 + *
   1.289 + * It takes no arguments.  The return value is a simple boolean:
   1.290 + *   PR_TRUE means encryption (or decryption) is *possible*
   1.291 + *	(but may still fail due to other reasons, like because we cannot
   1.292 + *	find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
   1.293 + *   PR_FALSE means encryption (or decryption) is not permitted
   1.294 + *
   1.295 + * There are no errors from this routine.
   1.296 + */
   1.297 +PRBool
   1.298 +NSS_SMIMEUtil_EncryptionPossible(void)
   1.299 +{
   1.300 +    int i;
   1.301 +
   1.302 +    for (i = 0; i < smime_cipher_map_count; i++) {
   1.303 +	if (smime_cipher_map[i].allowed)
   1.304 +	    return PR_TRUE;
   1.305 +    }
   1.306 +    return PR_FALSE;
   1.307 +}
   1.308 +
   1.309 +
   1.310 +static int
   1.311 +nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
   1.312 +{
   1.313 +    int i;
   1.314 +    SECOidTag capIDTag;
   1.315 +
   1.316 +    /* we need the OIDTag here */
   1.317 +    capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
   1.318 +
   1.319 +    /* go over all the SMIME ciphers we know and see if we find a match */
   1.320 +    for (i = 0; i < smime_cipher_map_count; i++) {
   1.321 +	if (smime_cipher_map[i].algtag != capIDTag)
   1.322 +	    continue;
   1.323 +	/*
   1.324 +	 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
   1.325 +	 * 2 NULLs as equal and NULL and non-NULL as not equal), we could
   1.326 +	 * use that here instead of all of the following comparison code.
   1.327 +	 */
   1.328 +	if (!smime_cipher_map[i].parms) { 
   1.329 +	    if (!cap->parameters.data || !cap->parameters.len)
   1.330 +		break;	/* both empty: bingo */
   1.331 +	    if (cap->parameters.len     == 2  &&
   1.332 +	        cap->parameters.data[0] == SEC_ASN1_NULL &&
   1.333 +		cap->parameters.data[1] == 0) 
   1.334 +		break;  /* DER NULL == NULL, bingo */
   1.335 +	} else if (cap->parameters.data != NULL && 
   1.336 +	    cap->parameters.len == smime_cipher_map[i].parms->len &&
   1.337 +	    PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
   1.338 +			     cap->parameters.len) == 0)
   1.339 +	{
   1.340 +	    break;	/* both not empty, same length & equal content: bingo */
   1.341 +	}
   1.342 +    }
   1.343 +
   1.344 +    if (i == smime_cipher_map_count)
   1.345 +	return 0;				/* no match found */
   1.346 +    return smime_cipher_map[i].cipher;	/* match found, point to cipher */
   1.347 +}
   1.348 +
   1.349 +/*
   1.350 + * smime_choose_cipher - choose a cipher that works for all the recipients
   1.351 + *
   1.352 + * "scert"  - sender's certificate
   1.353 + * "rcerts" - recipient's certificates
   1.354 + */
   1.355 +static long
   1.356 +smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
   1.357 +{
   1.358 +    PLArenaPool *poolp;
   1.359 +    long cipher;
   1.360 +    long chosen_cipher;
   1.361 +    int *cipher_abilities;
   1.362 +    int *cipher_votes;
   1.363 +    int weak_mapi;
   1.364 +    int strong_mapi;
   1.365 +    int aes128_mapi;
   1.366 +    int aes256_mapi;
   1.367 +    int rcount, mapi, max, i;
   1.368 +
   1.369 +    chosen_cipher = SMIME_RC2_CBC_40;		/* the default, LCD */
   1.370 +    weak_mapi = smime_mapi_by_cipher(chosen_cipher);
   1.371 +    aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
   1.372 +    aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
   1.373 +
   1.374 +    poolp = PORT_NewArena (1024);		/* XXX what is right value? */
   1.375 +    if (poolp == NULL)
   1.376 +	goto done;
   1.377 +
   1.378 +    cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
   1.379 +    cipher_votes     = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
   1.380 +    if (cipher_votes == NULL || cipher_abilities == NULL)
   1.381 +	goto done;
   1.382 +
   1.383 +    /* Make triple-DES the strong cipher. */
   1.384 +    strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
   1.385 +
   1.386 +    /* walk all the recipient's certs */
   1.387 +    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
   1.388 +	SECItem *profile;
   1.389 +	NSSSMIMECapability **caps;
   1.390 +	int pref;
   1.391 +
   1.392 +	/* the first cipher that matches in the user's SMIME profile gets
   1.393 +	 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
   1.394 +	 * and so on. If every cipher matches, the last one gets 1 (one) vote */
   1.395 +	pref = smime_cipher_map_count;
   1.396 +
   1.397 +	/* find recipient's SMIME profile */
   1.398 +	profile = CERT_FindSMimeProfile(rcerts[rcount]);
   1.399 +
   1.400 +	if (profile != NULL && profile->data != NULL && profile->len > 0) {
   1.401 +	    /* we have a profile (still DER-encoded) */
   1.402 +	    caps = NULL;
   1.403 +	    /* decode it */
   1.404 +	    if (SEC_QuickDERDecodeItem(poolp, &caps,
   1.405 +                    NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
   1.406 +		    caps != NULL)
   1.407 +	    {
   1.408 +		/* walk the SMIME capabilities for this recipient */
   1.409 +		for (i = 0; caps[i] != NULL; i++) {
   1.410 +		    cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
   1.411 +		    mapi = smime_mapi_by_cipher(cipher);
   1.412 +		    if (mapi >= 0) {
   1.413 +			/* found the cipher */
   1.414 +			cipher_abilities[mapi]++;
   1.415 +			cipher_votes[mapi] += pref;
   1.416 +			--pref;
   1.417 +		    }
   1.418 +		}
   1.419 +	    }
   1.420 +	} else {
   1.421 +	    /* no profile found - so we can only assume that the user can do
   1.422 +	     * the mandatory algorithms which are RC2-40 (weak crypto) and
   1.423 +	     * 3DES (strong crypto), unless the user has an elliptic curve
   1.424 +	     * key.  For elliptic curve keys, RFC 5753 mandates support
   1.425 +	     * for AES 128 CBC. */
   1.426 +	    SECKEYPublicKey *key;
   1.427 +	    unsigned int pklen_bits;
   1.428 +	    KeyType key_type;
   1.429 +
   1.430 +	    /*
   1.431 +	     * if recipient's public key length is > 512, vote for a strong cipher
   1.432 +	     * please not that the side effect of this is that if only one recipient
   1.433 +	     * has an export-level public key, the strong cipher is disabled.
   1.434 +	     *
   1.435 +	     * XXX This is probably only good for RSA keys.  What I would
   1.436 +	     * really like is a function to just say;  Is the public key in
   1.437 +	     * this cert an export-length key?  Then I would not have to
   1.438 +	     * know things like the value 512, or the kind of key, or what
   1.439 +	     * a subjectPublicKeyInfo is, etc.
   1.440 +	     */
   1.441 +	    key = CERT_ExtractPublicKey(rcerts[rcount]);
   1.442 +	    pklen_bits = 0;
   1.443 +	    if (key != NULL) {
   1.444 +		pklen_bits = SECKEY_PublicKeyStrengthInBits (key);
   1.445 +		key_type = SECKEY_GetPublicKeyType(key);
   1.446 +		SECKEY_DestroyPublicKey (key);
   1.447 +	    }
   1.448 +
   1.449 +	    if (key_type == ecKey) {
   1.450 +		/* While RFC 5753 mandates support for AES-128 CBC, should use
   1.451 +		 * AES 256 if user's key provides more than 128 bits of
   1.452 +		 * security strength so that symmetric key is not weak link. */
   1.453 +
   1.454 +		/* RC2-40 is not compatible with elliptic curve keys. */
   1.455 +		chosen_cipher = SMIME_DES_EDE3_168;
   1.456 +		if (pklen_bits > 256) {
   1.457 +		    cipher_abilities[aes256_mapi]++;
   1.458 +		    cipher_votes[aes256_mapi] += pref;
   1.459 +		    pref--;
   1.460 +		}
   1.461 +		cipher_abilities[aes128_mapi]++;
   1.462 +		cipher_votes[aes128_mapi] += pref;
   1.463 +		pref--;
   1.464 +		cipher_abilities[strong_mapi]++;
   1.465 +		cipher_votes[strong_mapi] += pref;
   1.466 +		pref--;
   1.467 +	    } else {
   1.468 +		if (pklen_bits > 512) {
   1.469 +		    /* cast votes for the strong algorithm */
   1.470 +		    cipher_abilities[strong_mapi]++;
   1.471 +		    cipher_votes[strong_mapi] += pref;
   1.472 +		    pref--;
   1.473 +		}
   1.474 +
   1.475 +		/* always cast (possibly less) votes for the weak algorithm */
   1.476 +		cipher_abilities[weak_mapi]++;
   1.477 +		cipher_votes[weak_mapi] += pref;
   1.478 +	    } 
   1.479 +	}
   1.480 +	if (profile != NULL)
   1.481 +	    SECITEM_FreeItem(profile, PR_TRUE);
   1.482 +    }
   1.483 +
   1.484 +    /* find cipher that is agreeable by all recipients and that has the most votes */
   1.485 +    max = 0;
   1.486 +    for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
   1.487 +	/* if not all of the recipients can do this, forget it */
   1.488 +	if (cipher_abilities[mapi] != rcount)
   1.489 +	    continue;
   1.490 +	/* if cipher is not enabled or not allowed by policy, forget it */
   1.491 +	if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
   1.492 +	    continue;
   1.493 +	/* now see if this one has more votes than the last best one */
   1.494 +	if (cipher_votes[mapi] >= max) {
   1.495 +	    /* if equal number of votes, prefer the ones further down in the list */
   1.496 +	    /* with the expectation that these are higher rated ciphers */
   1.497 +	    chosen_cipher = smime_cipher_map[mapi].cipher;
   1.498 +	    max = cipher_votes[mapi];
   1.499 +	}
   1.500 +    }
   1.501 +    /* if no common cipher was found, chosen_cipher stays at the default */
   1.502 +
   1.503 +done:
   1.504 +    if (poolp != NULL)
   1.505 +	PORT_FreeArena (poolp, PR_FALSE);
   1.506 +
   1.507 +    return chosen_cipher;
   1.508 +}
   1.509 +
   1.510 +/*
   1.511 + * XXX This is a hack for now to satisfy our current interface.
   1.512 + * Eventually, with more parameters needing to be specified, just
   1.513 + * looking up the keysize is not going to be sufficient.
   1.514 + */
   1.515 +static int
   1.516 +smime_keysize_by_cipher (unsigned long which)
   1.517 +{
   1.518 +    int keysize;
   1.519 +
   1.520 +    switch (which) {
   1.521 +      case SMIME_RC2_CBC_40:
   1.522 +	keysize = 40;
   1.523 +	break;
   1.524 +      case SMIME_RC2_CBC_64:
   1.525 +	keysize = 64;
   1.526 +	break;
   1.527 +      case SMIME_RC2_CBC_128:
   1.528 +      case SMIME_AES_CBC_128:
   1.529 +	keysize = 128;
   1.530 +	break;
   1.531 +      case SMIME_AES_CBC_256:
   1.532 +	keysize = 256;
   1.533 +	break;
   1.534 +      case SMIME_DES_CBC_56:
   1.535 +      case SMIME_DES_EDE3_168:
   1.536 +	/*
   1.537 +	 * These are special; since the key size is fixed, we actually
   1.538 +	 * want to *avoid* specifying a key size.
   1.539 +	 */
   1.540 +	keysize = 0;
   1.541 +	break;
   1.542 +      default:
   1.543 +	keysize = -1;
   1.544 +	break;
   1.545 +    }
   1.546 +
   1.547 +    return keysize;
   1.548 +}
   1.549 +
   1.550 +/*
   1.551 + * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
   1.552 + *
   1.553 + * it would be great for UI purposes if there would be a way to find out which recipients
   1.554 + * prevented a strong cipher from being used...
   1.555 + */
   1.556 +SECStatus
   1.557 +NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
   1.558 +{
   1.559 +    unsigned long cipher;
   1.560 +    int mapi;
   1.561 +
   1.562 +    cipher = smime_choose_cipher(NULL, rcerts);
   1.563 +    mapi = smime_mapi_by_cipher(cipher);
   1.564 +
   1.565 +    *bulkalgtag = smime_cipher_map[mapi].algtag;
   1.566 +    *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
   1.567 +
   1.568 +    return SECSuccess;
   1.569 +}
   1.570 +
   1.571 +/*
   1.572 + * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
   1.573 + *
   1.574 + * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
   1.575 + * S/MIME capabilities attribute value.
   1.576 + *
   1.577 + * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
   1.578 + * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
   1.579 + *
   1.580 + * "poolp" - arena pool to create the S/MIME capabilities data on
   1.581 + * "dest" - SECItem to put the data in
   1.582 + */
   1.583 +SECStatus
   1.584 +NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
   1.585 +{
   1.586 +    NSSSMIMECapability *cap;
   1.587 +    NSSSMIMECapability **smime_capabilities;
   1.588 +    smime_cipher_map_entry *map;
   1.589 +    SECOidData *oiddata;
   1.590 +    SECItem *dummy;
   1.591 +    int i, capIndex;
   1.592 +
   1.593 +    /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
   1.594 +    /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
   1.595 +    smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
   1.596 +				      * sizeof(NSSSMIMECapability *));
   1.597 +    if (smime_capabilities == NULL)
   1.598 +	return SECFailure;
   1.599 +
   1.600 +    capIndex = 0;
   1.601 +
   1.602 +    /* Add all the symmetric ciphers
   1.603 +     * We walk the cipher list backwards, as it is ordered by increasing strength,
   1.604 +     * we prefer the stronger cipher over a weaker one, and we have to list the
   1.605 +     * preferred algorithm first */
   1.606 +    for (i = smime_cipher_map_count - 1; i >= 0; i--) {
   1.607 +	/* Find the corresponding entry in the cipher map. */
   1.608 +	map = &(smime_cipher_map[i]);
   1.609 +	if (!map->enabled)
   1.610 +	    continue;
   1.611 +
   1.612 +	/* get next SMIME capability */
   1.613 +	cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
   1.614 +	if (cap == NULL)
   1.615 +	    break;
   1.616 +	smime_capabilities[capIndex++] = cap;
   1.617 +
   1.618 +	oiddata = SECOID_FindOIDByTag(map->algtag);
   1.619 +	if (oiddata == NULL)
   1.620 +	    break;
   1.621 +
   1.622 +	cap->capabilityID.data = oiddata->oid.data;
   1.623 +	cap->capabilityID.len = oiddata->oid.len;
   1.624 +	cap->parameters.data = map->parms ? map->parms->data : NULL;
   1.625 +	cap->parameters.len = map->parms ? map->parms->len : 0;
   1.626 +	cap->cipher = smime_cipher_map[i].cipher;
   1.627 +    }
   1.628 +
   1.629 +    /* XXX add signature algorithms */
   1.630 +    /* XXX add key encipherment algorithms */
   1.631 +
   1.632 +    smime_capabilities[capIndex] = NULL;	/* last one - now encode */
   1.633 +    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
   1.634 +
   1.635 +    /* now that we have the proper encoded SMIMECapabilities (or not),
   1.636 +     * free the work data */
   1.637 +    for (i = 0; smime_capabilities[i] != NULL; i++)
   1.638 +	PORT_Free(smime_capabilities[i]);
   1.639 +    PORT_Free(smime_capabilities);
   1.640 +
   1.641 +    return (dummy == NULL) ? SECFailure : SECSuccess;
   1.642 +}
   1.643 +
   1.644 +/*
   1.645 + * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
   1.646 + *
   1.647 + * "poolp" - arena pool to create the attr value on
   1.648 + * "dest" - SECItem to put the data in
   1.649 + * "cert" - certificate that should be marked as preferred encryption key
   1.650 + *          cert is expected to have been verified for EmailRecipient usage.
   1.651 + */
   1.652 +SECStatus
   1.653 +NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
   1.654 +{
   1.655 +    NSSSMIMEEncryptionKeyPreference ekp;
   1.656 +    SECItem *dummy = NULL;
   1.657 +    PLArenaPool *tmppoolp = NULL;
   1.658 +
   1.659 +    if (cert == NULL)
   1.660 +	goto loser;
   1.661 +
   1.662 +    tmppoolp = PORT_NewArena(1024);
   1.663 +    if (tmppoolp == NULL)
   1.664 +	goto loser;
   1.665 +
   1.666 +    /* XXX hardcoded IssuerSN choice for now */
   1.667 +    ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
   1.668 +    ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
   1.669 +    if (ekp.id.issuerAndSN == NULL)
   1.670 +	goto loser;
   1.671 +
   1.672 +    dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
   1.673 +
   1.674 +loser:
   1.675 +    if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
   1.676 +
   1.677 +    return (dummy == NULL) ? SECFailure : SECSuccess;
   1.678 +}
   1.679 +
   1.680 +/*
   1.681 + * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
   1.682 + *
   1.683 + * "poolp" - arena pool to create the attr value on
   1.684 + * "dest" - SECItem to put the data in
   1.685 + * "cert" - certificate that should be marked as preferred encryption key
   1.686 + *          cert is expected to have been verified for EmailRecipient usage.
   1.687 + */
   1.688 +SECStatus
   1.689 +NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
   1.690 +{
   1.691 +    SECItem *dummy = NULL;
   1.692 +    PLArenaPool *tmppoolp = NULL;
   1.693 +    CERTIssuerAndSN *isn;
   1.694 +
   1.695 +    if (cert == NULL)
   1.696 +	goto loser;
   1.697 +
   1.698 +    tmppoolp = PORT_NewArena(1024);
   1.699 +    if (tmppoolp == NULL)
   1.700 +	goto loser;
   1.701 +
   1.702 +    isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
   1.703 +    if (isn == NULL)
   1.704 +	goto loser;
   1.705 +
   1.706 +    dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
   1.707 +
   1.708 +loser:
   1.709 +    if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
   1.710 +
   1.711 +    return (dummy == NULL) ? SECFailure : SECSuccess;
   1.712 +}
   1.713 +
   1.714 +/*
   1.715 + * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
   1.716 + *				find cert marked by EncryptionKeyPreference attribute
   1.717 + *
   1.718 + * "certdb" - handle for the cert database to look in
   1.719 + * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
   1.720 + *
   1.721 + * if certificate is supposed to be found among the message's included certificates,
   1.722 + * they are assumed to have been imported already.
   1.723 + */
   1.724 +CERTCertificate *
   1.725 +NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
   1.726 +{
   1.727 +    PLArenaPool *tmppoolp = NULL;
   1.728 +    CERTCertificate *cert = NULL;
   1.729 +    NSSSMIMEEncryptionKeyPreference ekp;
   1.730 +
   1.731 +    tmppoolp = PORT_NewArena(1024);
   1.732 +    if (tmppoolp == NULL)
   1.733 +	return NULL;
   1.734 +
   1.735 +    /* decode DERekp */
   1.736 +    if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
   1.737 +                               DERekp) != SECSuccess)
   1.738 +	goto loser;
   1.739 +
   1.740 +    /* find cert */
   1.741 +    switch (ekp.selector) {
   1.742 +    case NSSSMIMEEncryptionKeyPref_IssuerSN:
   1.743 +	cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
   1.744 +	break;
   1.745 +    case NSSSMIMEEncryptionKeyPref_RKeyID:
   1.746 +    case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
   1.747 +	/* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
   1.748 +	break;
   1.749 +    default:
   1.750 +	PORT_Assert(0);
   1.751 +    }
   1.752 +loser:
   1.753 +    if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
   1.754 +
   1.755 +    return cert;
   1.756 +}
   1.757 +
   1.758 +extern const char __nss_smime_rcsid[];
   1.759 +extern const char __nss_smime_sccsid[];
   1.760 +
   1.761 +PRBool
   1.762 +NSSSMIME_VersionCheck(const char *importedVersion)
   1.763 +{
   1.764 +    /*
   1.765 +     * This is the secret handshake algorithm.
   1.766 +     *
   1.767 +     * This release has a simple version compatibility
   1.768 +     * check algorithm.  This release is not backward
   1.769 +     * compatible with previous major releases.  It is
   1.770 +     * not compatible with future major, minor, or
   1.771 +     * patch releases.
   1.772 +     */
   1.773 +    volatile char c; /* force a reference that won't get optimized away */
   1.774 +
   1.775 +    c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0]; 
   1.776 +
   1.777 +    return NSS_VersionCheck(importedVersion);
   1.778 +}
   1.779 +
   1.780 +const char *
   1.781 +NSSSMIME_GetVersion(void)
   1.782 +{
   1.783 +    return NSS_VERSION;
   1.784 +}

mercurial