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