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