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, ¶m_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, ¶m_int64, PR_TRUE, PR_TRUE }, 1.122 + { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, ¶m_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 +}