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