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