security/nss/lib/smime/smimeutil.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * Stuff specific to S/MIME policy and interoperability.
michael@0 7 */
michael@0 8
michael@0 9 #include "secmime.h"
michael@0 10 #include "secoid.h"
michael@0 11 #include "pk11func.h"
michael@0 12 #include "ciferfam.h" /* for CIPHER_FAMILY symbols */
michael@0 13 #include "secasn1.h"
michael@0 14 #include "secitem.h"
michael@0 15 #include "cert.h"
michael@0 16 #include "key.h"
michael@0 17 #include "secerr.h"
michael@0 18 #include "cms.h"
michael@0 19 #include "nss.h"
michael@0 20
michael@0 21 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
michael@0 22 SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
michael@0 23 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
michael@0 24
michael@0 25 /* various integer's ASN.1 encoding */
michael@0 26 static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
michael@0 27 static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
michael@0 28 static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
michael@0 29
michael@0 30 /* RC2 algorithm parameters (used in smime_cipher_map) */
michael@0 31 static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
michael@0 32 static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
michael@0 33 static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
michael@0 34
michael@0 35 /*
michael@0 36 * XXX Would like the "parameters" field to be a SECItem *, but the
michael@0 37 * encoder is having trouble with optional pointers to an ANY. Maybe
michael@0 38 * once that is fixed, can change this back...
michael@0 39 */
michael@0 40 typedef struct {
michael@0 41 SECItem capabilityID;
michael@0 42 SECItem parameters;
michael@0 43 long cipher; /* optimization */
michael@0 44 } NSSSMIMECapability;
michael@0 45
michael@0 46 static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
michael@0 47 { SEC_ASN1_SEQUENCE,
michael@0 48 0, NULL, sizeof(NSSSMIMECapability) },
michael@0 49 { SEC_ASN1_OBJECT_ID,
michael@0 50 offsetof(NSSSMIMECapability,capabilityID), },
michael@0 51 { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
michael@0 52 offsetof(NSSSMIMECapability,parameters), },
michael@0 53 { 0, }
michael@0 54 };
michael@0 55
michael@0 56 static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
michael@0 57 { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
michael@0 58 };
michael@0 59
michael@0 60 /*
michael@0 61 * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
michael@0 62 * to store this and only this certificate permanently for the sender email address.
michael@0 63 */
michael@0 64 typedef enum {
michael@0 65 NSSSMIMEEncryptionKeyPref_IssuerSN,
michael@0 66 NSSSMIMEEncryptionKeyPref_RKeyID,
michael@0 67 NSSSMIMEEncryptionKeyPref_SubjectKeyID
michael@0 68 } NSSSMIMEEncryptionKeyPrefSelector;
michael@0 69
michael@0 70 typedef struct {
michael@0 71 NSSSMIMEEncryptionKeyPrefSelector selector;
michael@0 72 union {
michael@0 73 CERTIssuerAndSN *issuerAndSN;
michael@0 74 NSSCMSRecipientKeyIdentifier *recipientKeyID;
michael@0 75 SECItem *subjectKeyID;
michael@0 76 } id;
michael@0 77 } NSSSMIMEEncryptionKeyPreference;
michael@0 78
michael@0 79 extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
michael@0 80
michael@0 81 static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
michael@0 82 { SEC_ASN1_CHOICE,
michael@0 83 offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
michael@0 84 sizeof(NSSSMIMEEncryptionKeyPreference) },
michael@0 85 { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0
michael@0 86 | SEC_ASN1_CONSTRUCTED,
michael@0 87 offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
michael@0 88 SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
michael@0 89 NSSSMIMEEncryptionKeyPref_IssuerSN },
michael@0 90 { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1
michael@0 91 | SEC_ASN1_CONSTRUCTED,
michael@0 92 offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
michael@0 93 NSSCMSRecipientKeyIdentifierTemplate,
michael@0 94 NSSSMIMEEncryptionKeyPref_RKeyID },
michael@0 95 { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2
michael@0 96 | SEC_ASN1_CONSTRUCTED,
michael@0 97 offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
michael@0 98 SEC_ASN1_SUB(SEC_OctetStringTemplate),
michael@0 99 NSSSMIMEEncryptionKeyPref_SubjectKeyID },
michael@0 100 { 0, }
michael@0 101 };
michael@0 102
michael@0 103 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
michael@0 104 typedef struct {
michael@0 105 unsigned long cipher;
michael@0 106 SECOidTag algtag;
michael@0 107 SECItem *parms;
michael@0 108 PRBool enabled; /* in the user's preferences */
michael@0 109 PRBool allowed; /* per export policy */
michael@0 110 } smime_cipher_map_entry;
michael@0 111
michael@0 112 /* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
michael@0 113 static smime_cipher_map_entry smime_cipher_map[] = {
michael@0 114 /* cipher algtag parms enabled allowed */
michael@0 115 /* ---------------------------------------------------------------------------------- */
michael@0 116 { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_int40, PR_TRUE, PR_TRUE },
michael@0 117 { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
michael@0 118 { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_TRUE, PR_TRUE },
michael@0 119 { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
michael@0 120 { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
michael@0 121 { SMIME_AES_CBC_128, SEC_OID_AES_128_CBC, NULL, PR_TRUE, PR_TRUE },
michael@0 122 { SMIME_AES_CBC_256, SEC_OID_AES_256_CBC, NULL, PR_TRUE, PR_TRUE }
michael@0 123 };
michael@0 124 static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
michael@0 125
michael@0 126 /*
michael@0 127 * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
michael@0 128 */
michael@0 129 static int
michael@0 130 smime_mapi_by_cipher(unsigned long cipher)
michael@0 131 {
michael@0 132 int i;
michael@0 133
michael@0 134 for (i = 0; i < smime_cipher_map_count; i++) {
michael@0 135 if (smime_cipher_map[i].cipher == cipher)
michael@0 136 return i; /* bingo */
michael@0 137 }
michael@0 138 return -1; /* should not happen if we're consistent, right? */
michael@0 139 }
michael@0 140
michael@0 141 /*
michael@0 142 * NSS_SMIME_EnableCipher - this function locally records the user's preference
michael@0 143 */
michael@0 144 SECStatus
michael@0 145 NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
michael@0 146 {
michael@0 147 unsigned long mask;
michael@0 148 int mapi;
michael@0 149
michael@0 150 mask = which & CIPHER_FAMILYID_MASK;
michael@0 151
michael@0 152 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
michael@0 153 if (mask != CIPHER_FAMILYID_SMIME)
michael@0 154 /* XXX set an error! */
michael@0 155 return SECFailure;
michael@0 156
michael@0 157 mapi = smime_mapi_by_cipher(which);
michael@0 158 if (mapi < 0)
michael@0 159 /* XXX set an error */
michael@0 160 return SECFailure;
michael@0 161
michael@0 162 /* do we try to turn on a forbidden cipher? */
michael@0 163 if (!smime_cipher_map[mapi].allowed && on) {
michael@0 164 PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
michael@0 165 return SECFailure;
michael@0 166 }
michael@0 167
michael@0 168 if (smime_cipher_map[mapi].enabled != on)
michael@0 169 smime_cipher_map[mapi].enabled = on;
michael@0 170
michael@0 171 return SECSuccess;
michael@0 172 }
michael@0 173
michael@0 174
michael@0 175 /*
michael@0 176 * this function locally records the export policy
michael@0 177 */
michael@0 178 SECStatus
michael@0 179 NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
michael@0 180 {
michael@0 181 unsigned long mask;
michael@0 182 int mapi;
michael@0 183
michael@0 184 mask = which & CIPHER_FAMILYID_MASK;
michael@0 185
michael@0 186 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
michael@0 187 if (mask != CIPHER_FAMILYID_SMIME)
michael@0 188 /* XXX set an error! */
michael@0 189 return SECFailure;
michael@0 190
michael@0 191 mapi = smime_mapi_by_cipher(which);
michael@0 192 if (mapi < 0)
michael@0 193 /* XXX set an error */
michael@0 194 return SECFailure;
michael@0 195
michael@0 196 if (smime_cipher_map[mapi].allowed != on)
michael@0 197 smime_cipher_map[mapi].allowed = on;
michael@0 198
michael@0 199 return SECSuccess;
michael@0 200 }
michael@0 201
michael@0 202 /*
michael@0 203 * Based on the given algorithm (including its parameters, in some cases!)
michael@0 204 * and the given key (may or may not be inspected, depending on the
michael@0 205 * algorithm), find the appropriate policy algorithm specification
michael@0 206 * and return it. If no match can be made, -1 is returned.
michael@0 207 */
michael@0 208 static SECStatus
michael@0 209 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
michael@0 210 {
michael@0 211 SECOidTag algtag;
michael@0 212 unsigned int keylen_bits;
michael@0 213 unsigned long c;
michael@0 214
michael@0 215 algtag = SECOID_GetAlgorithmTag(algid);
michael@0 216 switch (algtag) {
michael@0 217 case SEC_OID_RC2_CBC:
michael@0 218 keylen_bits = PK11_GetKeyStrength(key, algid);
michael@0 219 switch (keylen_bits) {
michael@0 220 case 40:
michael@0 221 c = SMIME_RC2_CBC_40;
michael@0 222 break;
michael@0 223 case 64:
michael@0 224 c = SMIME_RC2_CBC_64;
michael@0 225 break;
michael@0 226 case 128:
michael@0 227 c = SMIME_RC2_CBC_128;
michael@0 228 break;
michael@0 229 default:
michael@0 230 return SECFailure;
michael@0 231 }
michael@0 232 break;
michael@0 233 case SEC_OID_DES_CBC:
michael@0 234 c = SMIME_DES_CBC_56;
michael@0 235 break;
michael@0 236 case SEC_OID_DES_EDE3_CBC:
michael@0 237 c = SMIME_DES_EDE3_168;
michael@0 238 break;
michael@0 239 case SEC_OID_AES_128_CBC:
michael@0 240 c = SMIME_AES_CBC_128;
michael@0 241 break;
michael@0 242 case SEC_OID_AES_256_CBC:
michael@0 243 c = SMIME_AES_CBC_256;
michael@0 244 break;
michael@0 245 default:
michael@0 246 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
michael@0 247 return SECFailure;
michael@0 248 }
michael@0 249 *cipher = c;
michael@0 250 return SECSuccess;
michael@0 251 }
michael@0 252
michael@0 253 static PRBool
michael@0 254 nss_smime_cipher_allowed(unsigned long which)
michael@0 255 {
michael@0 256 int mapi;
michael@0 257
michael@0 258 mapi = smime_mapi_by_cipher(which);
michael@0 259 if (mapi < 0)
michael@0 260 return PR_FALSE;
michael@0 261 return smime_cipher_map[mapi].allowed;
michael@0 262 }
michael@0 263
michael@0 264 PRBool
michael@0 265 NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
michael@0 266 {
michael@0 267 unsigned long which;
michael@0 268
michael@0 269 if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
michael@0 270 return PR_FALSE;
michael@0 271
michael@0 272 return nss_smime_cipher_allowed(which);
michael@0 273 }
michael@0 274
michael@0 275
michael@0 276 /*
michael@0 277 * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
michael@0 278 *
michael@0 279 * This tells whether or not *any* S/MIME encryption can be done,
michael@0 280 * according to policy. Callers may use this to do nicer user interface
michael@0 281 * (say, greying out a checkbox so a user does not even try to encrypt
michael@0 282 * a message when they are not allowed to) or for any reason they want
michael@0 283 * to check whether S/MIME encryption (or decryption, for that matter)
michael@0 284 * may be done.
michael@0 285 *
michael@0 286 * It takes no arguments. The return value is a simple boolean:
michael@0 287 * PR_TRUE means encryption (or decryption) is *possible*
michael@0 288 * (but may still fail due to other reasons, like because we cannot
michael@0 289 * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
michael@0 290 * PR_FALSE means encryption (or decryption) is not permitted
michael@0 291 *
michael@0 292 * There are no errors from this routine.
michael@0 293 */
michael@0 294 PRBool
michael@0 295 NSS_SMIMEUtil_EncryptionPossible(void)
michael@0 296 {
michael@0 297 int i;
michael@0 298
michael@0 299 for (i = 0; i < smime_cipher_map_count; i++) {
michael@0 300 if (smime_cipher_map[i].allowed)
michael@0 301 return PR_TRUE;
michael@0 302 }
michael@0 303 return PR_FALSE;
michael@0 304 }
michael@0 305
michael@0 306
michael@0 307 static int
michael@0 308 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
michael@0 309 {
michael@0 310 int i;
michael@0 311 SECOidTag capIDTag;
michael@0 312
michael@0 313 /* we need the OIDTag here */
michael@0 314 capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
michael@0 315
michael@0 316 /* go over all the SMIME ciphers we know and see if we find a match */
michael@0 317 for (i = 0; i < smime_cipher_map_count; i++) {
michael@0 318 if (smime_cipher_map[i].algtag != capIDTag)
michael@0 319 continue;
michael@0 320 /*
michael@0 321 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
michael@0 322 * 2 NULLs as equal and NULL and non-NULL as not equal), we could
michael@0 323 * use that here instead of all of the following comparison code.
michael@0 324 */
michael@0 325 if (!smime_cipher_map[i].parms) {
michael@0 326 if (!cap->parameters.data || !cap->parameters.len)
michael@0 327 break; /* both empty: bingo */
michael@0 328 if (cap->parameters.len == 2 &&
michael@0 329 cap->parameters.data[0] == SEC_ASN1_NULL &&
michael@0 330 cap->parameters.data[1] == 0)
michael@0 331 break; /* DER NULL == NULL, bingo */
michael@0 332 } else if (cap->parameters.data != NULL &&
michael@0 333 cap->parameters.len == smime_cipher_map[i].parms->len &&
michael@0 334 PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
michael@0 335 cap->parameters.len) == 0)
michael@0 336 {
michael@0 337 break; /* both not empty, same length & equal content: bingo */
michael@0 338 }
michael@0 339 }
michael@0 340
michael@0 341 if (i == smime_cipher_map_count)
michael@0 342 return 0; /* no match found */
michael@0 343 return smime_cipher_map[i].cipher; /* match found, point to cipher */
michael@0 344 }
michael@0 345
michael@0 346 /*
michael@0 347 * smime_choose_cipher - choose a cipher that works for all the recipients
michael@0 348 *
michael@0 349 * "scert" - sender's certificate
michael@0 350 * "rcerts" - recipient's certificates
michael@0 351 */
michael@0 352 static long
michael@0 353 smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
michael@0 354 {
michael@0 355 PLArenaPool *poolp;
michael@0 356 long cipher;
michael@0 357 long chosen_cipher;
michael@0 358 int *cipher_abilities;
michael@0 359 int *cipher_votes;
michael@0 360 int weak_mapi;
michael@0 361 int strong_mapi;
michael@0 362 int aes128_mapi;
michael@0 363 int aes256_mapi;
michael@0 364 int rcount, mapi, max, i;
michael@0 365
michael@0 366 chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
michael@0 367 weak_mapi = smime_mapi_by_cipher(chosen_cipher);
michael@0 368 aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
michael@0 369 aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
michael@0 370
michael@0 371 poolp = PORT_NewArena (1024); /* XXX what is right value? */
michael@0 372 if (poolp == NULL)
michael@0 373 goto done;
michael@0 374
michael@0 375 cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
michael@0 376 cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
michael@0 377 if (cipher_votes == NULL || cipher_abilities == NULL)
michael@0 378 goto done;
michael@0 379
michael@0 380 /* Make triple-DES the strong cipher. */
michael@0 381 strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
michael@0 382
michael@0 383 /* walk all the recipient's certs */
michael@0 384 for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
michael@0 385 SECItem *profile;
michael@0 386 NSSSMIMECapability **caps;
michael@0 387 int pref;
michael@0 388
michael@0 389 /* the first cipher that matches in the user's SMIME profile gets
michael@0 390 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
michael@0 391 * and so on. If every cipher matches, the last one gets 1 (one) vote */
michael@0 392 pref = smime_cipher_map_count;
michael@0 393
michael@0 394 /* find recipient's SMIME profile */
michael@0 395 profile = CERT_FindSMimeProfile(rcerts[rcount]);
michael@0 396
michael@0 397 if (profile != NULL && profile->data != NULL && profile->len > 0) {
michael@0 398 /* we have a profile (still DER-encoded) */
michael@0 399 caps = NULL;
michael@0 400 /* decode it */
michael@0 401 if (SEC_QuickDERDecodeItem(poolp, &caps,
michael@0 402 NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
michael@0 403 caps != NULL)
michael@0 404 {
michael@0 405 /* walk the SMIME capabilities for this recipient */
michael@0 406 for (i = 0; caps[i] != NULL; i++) {
michael@0 407 cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
michael@0 408 mapi = smime_mapi_by_cipher(cipher);
michael@0 409 if (mapi >= 0) {
michael@0 410 /* found the cipher */
michael@0 411 cipher_abilities[mapi]++;
michael@0 412 cipher_votes[mapi] += pref;
michael@0 413 --pref;
michael@0 414 }
michael@0 415 }
michael@0 416 }
michael@0 417 } else {
michael@0 418 /* no profile found - so we can only assume that the user can do
michael@0 419 * the mandatory algorithms which are RC2-40 (weak crypto) and
michael@0 420 * 3DES (strong crypto), unless the user has an elliptic curve
michael@0 421 * key. For elliptic curve keys, RFC 5753 mandates support
michael@0 422 * for AES 128 CBC. */
michael@0 423 SECKEYPublicKey *key;
michael@0 424 unsigned int pklen_bits;
michael@0 425 KeyType key_type;
michael@0 426
michael@0 427 /*
michael@0 428 * if recipient's public key length is > 512, vote for a strong cipher
michael@0 429 * please not that the side effect of this is that if only one recipient
michael@0 430 * has an export-level public key, the strong cipher is disabled.
michael@0 431 *
michael@0 432 * XXX This is probably only good for RSA keys. What I would
michael@0 433 * really like is a function to just say; Is the public key in
michael@0 434 * this cert an export-length key? Then I would not have to
michael@0 435 * know things like the value 512, or the kind of key, or what
michael@0 436 * a subjectPublicKeyInfo is, etc.
michael@0 437 */
michael@0 438 key = CERT_ExtractPublicKey(rcerts[rcount]);
michael@0 439 pklen_bits = 0;
michael@0 440 if (key != NULL) {
michael@0 441 pklen_bits = SECKEY_PublicKeyStrengthInBits (key);
michael@0 442 key_type = SECKEY_GetPublicKeyType(key);
michael@0 443 SECKEY_DestroyPublicKey (key);
michael@0 444 }
michael@0 445
michael@0 446 if (key_type == ecKey) {
michael@0 447 /* While RFC 5753 mandates support for AES-128 CBC, should use
michael@0 448 * AES 256 if user's key provides more than 128 bits of
michael@0 449 * security strength so that symmetric key is not weak link. */
michael@0 450
michael@0 451 /* RC2-40 is not compatible with elliptic curve keys. */
michael@0 452 chosen_cipher = SMIME_DES_EDE3_168;
michael@0 453 if (pklen_bits > 256) {
michael@0 454 cipher_abilities[aes256_mapi]++;
michael@0 455 cipher_votes[aes256_mapi] += pref;
michael@0 456 pref--;
michael@0 457 }
michael@0 458 cipher_abilities[aes128_mapi]++;
michael@0 459 cipher_votes[aes128_mapi] += pref;
michael@0 460 pref--;
michael@0 461 cipher_abilities[strong_mapi]++;
michael@0 462 cipher_votes[strong_mapi] += pref;
michael@0 463 pref--;
michael@0 464 } else {
michael@0 465 if (pklen_bits > 512) {
michael@0 466 /* cast votes for the strong algorithm */
michael@0 467 cipher_abilities[strong_mapi]++;
michael@0 468 cipher_votes[strong_mapi] += pref;
michael@0 469 pref--;
michael@0 470 }
michael@0 471
michael@0 472 /* always cast (possibly less) votes for the weak algorithm */
michael@0 473 cipher_abilities[weak_mapi]++;
michael@0 474 cipher_votes[weak_mapi] += pref;
michael@0 475 }
michael@0 476 }
michael@0 477 if (profile != NULL)
michael@0 478 SECITEM_FreeItem(profile, PR_TRUE);
michael@0 479 }
michael@0 480
michael@0 481 /* find cipher that is agreeable by all recipients and that has the most votes */
michael@0 482 max = 0;
michael@0 483 for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
michael@0 484 /* if not all of the recipients can do this, forget it */
michael@0 485 if (cipher_abilities[mapi] != rcount)
michael@0 486 continue;
michael@0 487 /* if cipher is not enabled or not allowed by policy, forget it */
michael@0 488 if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
michael@0 489 continue;
michael@0 490 /* now see if this one has more votes than the last best one */
michael@0 491 if (cipher_votes[mapi] >= max) {
michael@0 492 /* if equal number of votes, prefer the ones further down in the list */
michael@0 493 /* with the expectation that these are higher rated ciphers */
michael@0 494 chosen_cipher = smime_cipher_map[mapi].cipher;
michael@0 495 max = cipher_votes[mapi];
michael@0 496 }
michael@0 497 }
michael@0 498 /* if no common cipher was found, chosen_cipher stays at the default */
michael@0 499
michael@0 500 done:
michael@0 501 if (poolp != NULL)
michael@0 502 PORT_FreeArena (poolp, PR_FALSE);
michael@0 503
michael@0 504 return chosen_cipher;
michael@0 505 }
michael@0 506
michael@0 507 /*
michael@0 508 * XXX This is a hack for now to satisfy our current interface.
michael@0 509 * Eventually, with more parameters needing to be specified, just
michael@0 510 * looking up the keysize is not going to be sufficient.
michael@0 511 */
michael@0 512 static int
michael@0 513 smime_keysize_by_cipher (unsigned long which)
michael@0 514 {
michael@0 515 int keysize;
michael@0 516
michael@0 517 switch (which) {
michael@0 518 case SMIME_RC2_CBC_40:
michael@0 519 keysize = 40;
michael@0 520 break;
michael@0 521 case SMIME_RC2_CBC_64:
michael@0 522 keysize = 64;
michael@0 523 break;
michael@0 524 case SMIME_RC2_CBC_128:
michael@0 525 case SMIME_AES_CBC_128:
michael@0 526 keysize = 128;
michael@0 527 break;
michael@0 528 case SMIME_AES_CBC_256:
michael@0 529 keysize = 256;
michael@0 530 break;
michael@0 531 case SMIME_DES_CBC_56:
michael@0 532 case SMIME_DES_EDE3_168:
michael@0 533 /*
michael@0 534 * These are special; since the key size is fixed, we actually
michael@0 535 * want to *avoid* specifying a key size.
michael@0 536 */
michael@0 537 keysize = 0;
michael@0 538 break;
michael@0 539 default:
michael@0 540 keysize = -1;
michael@0 541 break;
michael@0 542 }
michael@0 543
michael@0 544 return keysize;
michael@0 545 }
michael@0 546
michael@0 547 /*
michael@0 548 * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
michael@0 549 *
michael@0 550 * it would be great for UI purposes if there would be a way to find out which recipients
michael@0 551 * prevented a strong cipher from being used...
michael@0 552 */
michael@0 553 SECStatus
michael@0 554 NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
michael@0 555 {
michael@0 556 unsigned long cipher;
michael@0 557 int mapi;
michael@0 558
michael@0 559 cipher = smime_choose_cipher(NULL, rcerts);
michael@0 560 mapi = smime_mapi_by_cipher(cipher);
michael@0 561
michael@0 562 *bulkalgtag = smime_cipher_map[mapi].algtag;
michael@0 563 *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
michael@0 564
michael@0 565 return SECSuccess;
michael@0 566 }
michael@0 567
michael@0 568 /*
michael@0 569 * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
michael@0 570 *
michael@0 571 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
michael@0 572 * S/MIME capabilities attribute value.
michael@0 573 *
michael@0 574 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
michael@0 575 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
michael@0 576 *
michael@0 577 * "poolp" - arena pool to create the S/MIME capabilities data on
michael@0 578 * "dest" - SECItem to put the data in
michael@0 579 */
michael@0 580 SECStatus
michael@0 581 NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
michael@0 582 {
michael@0 583 NSSSMIMECapability *cap;
michael@0 584 NSSSMIMECapability **smime_capabilities;
michael@0 585 smime_cipher_map_entry *map;
michael@0 586 SECOidData *oiddata;
michael@0 587 SECItem *dummy;
michael@0 588 int i, capIndex;
michael@0 589
michael@0 590 /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
michael@0 591 /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
michael@0 592 smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
michael@0 593 * sizeof(NSSSMIMECapability *));
michael@0 594 if (smime_capabilities == NULL)
michael@0 595 return SECFailure;
michael@0 596
michael@0 597 capIndex = 0;
michael@0 598
michael@0 599 /* Add all the symmetric ciphers
michael@0 600 * We walk the cipher list backwards, as it is ordered by increasing strength,
michael@0 601 * we prefer the stronger cipher over a weaker one, and we have to list the
michael@0 602 * preferred algorithm first */
michael@0 603 for (i = smime_cipher_map_count - 1; i >= 0; i--) {
michael@0 604 /* Find the corresponding entry in the cipher map. */
michael@0 605 map = &(smime_cipher_map[i]);
michael@0 606 if (!map->enabled)
michael@0 607 continue;
michael@0 608
michael@0 609 /* get next SMIME capability */
michael@0 610 cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
michael@0 611 if (cap == NULL)
michael@0 612 break;
michael@0 613 smime_capabilities[capIndex++] = cap;
michael@0 614
michael@0 615 oiddata = SECOID_FindOIDByTag(map->algtag);
michael@0 616 if (oiddata == NULL)
michael@0 617 break;
michael@0 618
michael@0 619 cap->capabilityID.data = oiddata->oid.data;
michael@0 620 cap->capabilityID.len = oiddata->oid.len;
michael@0 621 cap->parameters.data = map->parms ? map->parms->data : NULL;
michael@0 622 cap->parameters.len = map->parms ? map->parms->len : 0;
michael@0 623 cap->cipher = smime_cipher_map[i].cipher;
michael@0 624 }
michael@0 625
michael@0 626 /* XXX add signature algorithms */
michael@0 627 /* XXX add key encipherment algorithms */
michael@0 628
michael@0 629 smime_capabilities[capIndex] = NULL; /* last one - now encode */
michael@0 630 dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
michael@0 631
michael@0 632 /* now that we have the proper encoded SMIMECapabilities (or not),
michael@0 633 * free the work data */
michael@0 634 for (i = 0; smime_capabilities[i] != NULL; i++)
michael@0 635 PORT_Free(smime_capabilities[i]);
michael@0 636 PORT_Free(smime_capabilities);
michael@0 637
michael@0 638 return (dummy == NULL) ? SECFailure : SECSuccess;
michael@0 639 }
michael@0 640
michael@0 641 /*
michael@0 642 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
michael@0 643 *
michael@0 644 * "poolp" - arena pool to create the attr value on
michael@0 645 * "dest" - SECItem to put the data in
michael@0 646 * "cert" - certificate that should be marked as preferred encryption key
michael@0 647 * cert is expected to have been verified for EmailRecipient usage.
michael@0 648 */
michael@0 649 SECStatus
michael@0 650 NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
michael@0 651 {
michael@0 652 NSSSMIMEEncryptionKeyPreference ekp;
michael@0 653 SECItem *dummy = NULL;
michael@0 654 PLArenaPool *tmppoolp = NULL;
michael@0 655
michael@0 656 if (cert == NULL)
michael@0 657 goto loser;
michael@0 658
michael@0 659 tmppoolp = PORT_NewArena(1024);
michael@0 660 if (tmppoolp == NULL)
michael@0 661 goto loser;
michael@0 662
michael@0 663 /* XXX hardcoded IssuerSN choice for now */
michael@0 664 ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
michael@0 665 ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
michael@0 666 if (ekp.id.issuerAndSN == NULL)
michael@0 667 goto loser;
michael@0 668
michael@0 669 dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
michael@0 670
michael@0 671 loser:
michael@0 672 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
michael@0 673
michael@0 674 return (dummy == NULL) ? SECFailure : SECSuccess;
michael@0 675 }
michael@0 676
michael@0 677 /*
michael@0 678 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
michael@0 679 *
michael@0 680 * "poolp" - arena pool to create the attr value on
michael@0 681 * "dest" - SECItem to put the data in
michael@0 682 * "cert" - certificate that should be marked as preferred encryption key
michael@0 683 * cert is expected to have been verified for EmailRecipient usage.
michael@0 684 */
michael@0 685 SECStatus
michael@0 686 NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
michael@0 687 {
michael@0 688 SECItem *dummy = NULL;
michael@0 689 PLArenaPool *tmppoolp = NULL;
michael@0 690 CERTIssuerAndSN *isn;
michael@0 691
michael@0 692 if (cert == NULL)
michael@0 693 goto loser;
michael@0 694
michael@0 695 tmppoolp = PORT_NewArena(1024);
michael@0 696 if (tmppoolp == NULL)
michael@0 697 goto loser;
michael@0 698
michael@0 699 isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
michael@0 700 if (isn == NULL)
michael@0 701 goto loser;
michael@0 702
michael@0 703 dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
michael@0 704
michael@0 705 loser:
michael@0 706 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
michael@0 707
michael@0 708 return (dummy == NULL) ? SECFailure : SECSuccess;
michael@0 709 }
michael@0 710
michael@0 711 /*
michael@0 712 * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
michael@0 713 * find cert marked by EncryptionKeyPreference attribute
michael@0 714 *
michael@0 715 * "certdb" - handle for the cert database to look in
michael@0 716 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
michael@0 717 *
michael@0 718 * if certificate is supposed to be found among the message's included certificates,
michael@0 719 * they are assumed to have been imported already.
michael@0 720 */
michael@0 721 CERTCertificate *
michael@0 722 NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
michael@0 723 {
michael@0 724 PLArenaPool *tmppoolp = NULL;
michael@0 725 CERTCertificate *cert = NULL;
michael@0 726 NSSSMIMEEncryptionKeyPreference ekp;
michael@0 727
michael@0 728 tmppoolp = PORT_NewArena(1024);
michael@0 729 if (tmppoolp == NULL)
michael@0 730 return NULL;
michael@0 731
michael@0 732 /* decode DERekp */
michael@0 733 if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
michael@0 734 DERekp) != SECSuccess)
michael@0 735 goto loser;
michael@0 736
michael@0 737 /* find cert */
michael@0 738 switch (ekp.selector) {
michael@0 739 case NSSSMIMEEncryptionKeyPref_IssuerSN:
michael@0 740 cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
michael@0 741 break;
michael@0 742 case NSSSMIMEEncryptionKeyPref_RKeyID:
michael@0 743 case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
michael@0 744 /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
michael@0 745 break;
michael@0 746 default:
michael@0 747 PORT_Assert(0);
michael@0 748 }
michael@0 749 loser:
michael@0 750 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
michael@0 751
michael@0 752 return cert;
michael@0 753 }
michael@0 754
michael@0 755 extern const char __nss_smime_rcsid[];
michael@0 756 extern const char __nss_smime_sccsid[];
michael@0 757
michael@0 758 PRBool
michael@0 759 NSSSMIME_VersionCheck(const char *importedVersion)
michael@0 760 {
michael@0 761 /*
michael@0 762 * This is the secret handshake algorithm.
michael@0 763 *
michael@0 764 * This release has a simple version compatibility
michael@0 765 * check algorithm. This release is not backward
michael@0 766 * compatible with previous major releases. It is
michael@0 767 * not compatible with future major, minor, or
michael@0 768 * patch releases.
michael@0 769 */
michael@0 770 volatile char c; /* force a reference that won't get optimized away */
michael@0 771
michael@0 772 c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0];
michael@0 773
michael@0 774 return NSS_VersionCheck(importedVersion);
michael@0 775 }
michael@0 776
michael@0 777 const char *
michael@0 778 NSSSMIME_GetVersion(void)
michael@0 779 {
michael@0 780 return NSS_VERSION;
michael@0 781 }

mercurial