Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 /*
6 * Stuff specific to S/MIME policy and interoperability.
7 */
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"
21 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
22 SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
23 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
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 };
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) };
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;
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 };
56 static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
57 { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
58 };
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;
70 typedef struct {
71 NSSSMIMEEncryptionKeyPrefSelector selector;
72 union {
73 CERTIssuerAndSN *issuerAndSN;
74 NSSCMSRecipientKeyIdentifier *recipientKeyID;
75 SECItem *subjectKeyID;
76 } id;
77 } NSSSMIMEEncryptionKeyPreference;
79 extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
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 };
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;
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);
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;
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 }
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;
150 mask = which & CIPHER_FAMILYID_MASK;
152 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
153 if (mask != CIPHER_FAMILYID_SMIME)
154 /* XXX set an error! */
155 return SECFailure;
157 mapi = smime_mapi_by_cipher(which);
158 if (mapi < 0)
159 /* XXX set an error */
160 return SECFailure;
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 }
168 if (smime_cipher_map[mapi].enabled != on)
169 smime_cipher_map[mapi].enabled = on;
171 return SECSuccess;
172 }
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;
184 mask = which & CIPHER_FAMILYID_MASK;
186 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
187 if (mask != CIPHER_FAMILYID_SMIME)
188 /* XXX set an error! */
189 return SECFailure;
191 mapi = smime_mapi_by_cipher(which);
192 if (mapi < 0)
193 /* XXX set an error */
194 return SECFailure;
196 if (smime_cipher_map[mapi].allowed != on)
197 smime_cipher_map[mapi].allowed = on;
199 return SECSuccess;
200 }
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;
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 }
253 static PRBool
254 nss_smime_cipher_allowed(unsigned long which)
255 {
256 int mapi;
258 mapi = smime_mapi_by_cipher(which);
259 if (mapi < 0)
260 return PR_FALSE;
261 return smime_cipher_map[mapi].allowed;
262 }
264 PRBool
265 NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
266 {
267 unsigned long which;
269 if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
270 return PR_FALSE;
272 return nss_smime_cipher_allowed(which);
273 }
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;
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 }
307 static int
308 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
309 {
310 int i;
311 SECOidTag capIDTag;
313 /* we need the OIDTag here */
314 capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
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 }
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 }
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;
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);
371 poolp = PORT_NewArena (1024); /* XXX what is right value? */
372 if (poolp == NULL)
373 goto done;
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;
380 /* Make triple-DES the strong cipher. */
381 strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
383 /* walk all the recipient's certs */
384 for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
385 SECItem *profile;
386 NSSSMIMECapability **caps;
387 int pref;
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;
394 /* find recipient's SMIME profile */
395 profile = CERT_FindSMimeProfile(rcerts[rcount]);
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;
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 }
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. */
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 }
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 }
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 */
500 done:
501 if (poolp != NULL)
502 PORT_FreeArena (poolp, PR_FALSE);
504 return chosen_cipher;
505 }
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;
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 }
544 return keysize;
545 }
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;
559 cipher = smime_choose_cipher(NULL, rcerts);
560 mapi = smime_mapi_by_cipher(cipher);
562 *bulkalgtag = smime_cipher_map[mapi].algtag;
563 *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
565 return SECSuccess;
566 }
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;
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;
597 capIndex = 0;
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;
609 /* get next SMIME capability */
610 cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
611 if (cap == NULL)
612 break;
613 smime_capabilities[capIndex++] = cap;
615 oiddata = SECOID_FindOIDByTag(map->algtag);
616 if (oiddata == NULL)
617 break;
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 }
626 /* XXX add signature algorithms */
627 /* XXX add key encipherment algorithms */
629 smime_capabilities[capIndex] = NULL; /* last one - now encode */
630 dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
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);
638 return (dummy == NULL) ? SECFailure : SECSuccess;
639 }
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;
656 if (cert == NULL)
657 goto loser;
659 tmppoolp = PORT_NewArena(1024);
660 if (tmppoolp == NULL)
661 goto loser;
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;
669 dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
671 loser:
672 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
674 return (dummy == NULL) ? SECFailure : SECSuccess;
675 }
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;
692 if (cert == NULL)
693 goto loser;
695 tmppoolp = PORT_NewArena(1024);
696 if (tmppoolp == NULL)
697 goto loser;
699 isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
700 if (isn == NULL)
701 goto loser;
703 dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
705 loser:
706 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
708 return (dummy == NULL) ? SECFailure : SECSuccess;
709 }
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;
728 tmppoolp = PORT_NewArena(1024);
729 if (tmppoolp == NULL)
730 return NULL;
732 /* decode DERekp */
733 if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
734 DERekp) != SECSuccess)
735 goto loser;
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);
752 return cert;
753 }
755 extern const char __nss_smime_rcsid[];
756 extern const char __nss_smime_sccsid[];
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 */
772 c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0];
774 return NSS_VersionCheck(importedVersion);
775 }
777 const char *
778 NSSSMIME_GetVersion(void)
779 {
780 return NSS_VERSION;
781 }