]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/smimeutil.c
Security-57740.1.18.tar.gz
[apple/security.git] / libsecurity_smime / lib / smimeutil.c
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"
41 #include "SecAsn1Item.h"
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) */
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 };
63
64 /*
65 * XXX Would like the "parameters" field to be a SecAsn1Item * , but the
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 {
70 SecAsn1Item capabilityID;
71 SecAsn1Item parameters;
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;
104 SecAsn1Item *subjectKeyID;
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;
133 SecAsn1Item *parms;
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 /* ---------------------------------------------------------------------------------- */
142 { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_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, &param_int64, PR_FALSE, PR_FALSE },
145 { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_FALSE, PR_FALSE },
146 { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
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 },
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
173 SecSMIMEEnableCipher(unsigned long which, Boolean on)
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
207 SecSMIMEAllowCipher(unsigned long which, Boolean on)
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;
240 CFIndex keylen_bits;
241 unsigned long c;
242
243 algtag = SECOID_GetAlgorithmTag(algid);
244 switch (algtag) {
245 case SEC_OID_RC2_CBC:
246 #if USE_CDSA_CRYPTO
247 if (SecKeyGetStrengthInBits(key, algid, &keylen_bits))
248 return SECFailure;
249 #else
250 keylen_bits = CFDataGetLength((CFDataRef)key) * 8;
251 #endif
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;
269 case SEC_OID_FORTEZZA_SKIPJACK:
270 c = SMIME_FORTEZZA;
271 break;
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;
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;
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
345 static unsigned long
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 static int smime_keysize_by_cipher (unsigned long which);
382
383 /*
384 * smime_choose_cipher - choose a cipher that works for all the recipients
385 *
386 * "scert" - sender's certificate
387 * "rcerts" - recipient's certificates
388 */
389 static long
390 smime_choose_cipher(SecCertificateRef scert, SecCertificateRef *rcerts)
391 {
392 PRArenaPool *poolp;
393 long cipher;
394 long chosen_cipher;
395 int *cipher_abilities;
396 int *cipher_votes;
397 int weak_mapi;
398 int strong_mapi;
399 int rcount, mapi, max, i;
400 #if 1
401 // @@@ We Don't support Fortezza yet.
402 Boolean scert_is_fortezza = PR_FALSE;
403 #else
404 Boolean scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
405 #endif
406
407 chosen_cipher = SMIME_DES_CBC_56; /* the default, LCD */
408 weak_mapi = smime_mapi_by_cipher(chosen_cipher);
409
410 poolp = PORT_NewArena (1024); /* XXX what is right value? */
411 if (poolp == NULL)
412 goto done;
413
414 cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
415 cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
416 if (cipher_votes == NULL || cipher_abilities == NULL)
417 goto done;
418
419 /* If the user has the Fortezza preference turned on, make
420 * that the strong cipher. Otherwise, use triple-DES. */
421 strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
422 if (scert_is_fortezza) {
423 mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
424 if (mapi >= 0 && smime_cipher_map[mapi].enabled)
425 strong_mapi = mapi;
426 }
427
428 /* walk all the recipient's certs */
429 for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
430 SecAsn1Item *profile;
431 NSSSMIMECapability **caps;
432 int pref;
433
434 /* the first cipher that matches in the user's SMIME profile gets
435 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
436 * and so on. If every cipher matches, the last one gets 1 (one) vote */
437 pref = smime_cipher_map_count;
438
439 /* find recipient's SMIME profile */
440 profile = CERT_FindSMimeProfile(rcerts[rcount]);
441
442 if (profile != NULL && profile->Data != NULL && profile->Length > 0) {
443 /* we have a profile (still DER-encoded) */
444 caps = NULL;
445 /* decode it */
446 if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
447 caps != NULL)
448 {
449 /* walk the SMIME capabilities for this recipient */
450 for (i = 0; caps[i] != NULL; i++) {
451 cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
452 mapi = smime_mapi_by_cipher(cipher);
453 if (mapi >= 0) {
454 /* found the cipher */
455 cipher_abilities[mapi]++;
456 cipher_votes[mapi] += pref;
457 --pref;
458 }
459 }
460 }
461 } else {
462 /* no profile found - so we can only assume that the user can do
463 * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
464 SecPublicKeyRef key;
465 size_t pklen_bits;
466
467 /*
468 * if recipient's public key length is > 512, vote for a strong cipher
469 * please not that the side effect of this is that if only one recipient
470 * has an export-level public key, the strong cipher is disabled.
471 *
472 * XXX This is probably only good for RSA keys. What I would
473 * really like is a function to just say; Is the public key in
474 * this cert an export-length key? Then I would not have to
475 * know things like the value 512, or the kind of key, or what
476 * a subjectPublicKeyInfo is, etc.
477 */
478 key = CERT_ExtractPublicKey(rcerts[rcount]);
479 pklen_bits = 0;
480 if (key != NULL) {
481 #if USE_CDSA_CRYPTO
482 SecKeyGetStrengthInBits(key, NULL, &pklen_bits);
483 #else
484 pklen_bits = SecKeyGetSize(key, kSecKeyKeySizeInBits);
485 #endif
486 SECKEY_DestroyPublicKey (key);
487 }
488
489 if (pklen_bits > 512) {
490 /* cast votes for the strong algorithm */
491 cipher_abilities[strong_mapi]++;
492 cipher_votes[strong_mapi] += pref;
493 pref--;
494 }
495
496 /* always cast (possibly less) votes for the weak algorithm */
497 cipher_abilities[weak_mapi]++;
498 cipher_votes[weak_mapi] += pref;
499 }
500 if (profile != NULL)
501 SECITEM_FreeItem(profile, PR_TRUE);
502 }
503
504 /* find cipher that is agreeable by all recipients and that has the most votes */
505 max = 0;
506 for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
507 /* if not all of the recipients can do this, forget it */
508 if (cipher_abilities[mapi] != rcount)
509 continue;
510 /* if cipher is not enabled or not allowed by policy, forget it */
511 if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
512 continue;
513 /* if we're not doing fortezza, but the cipher is fortezza, forget it */
514 if (!scert_is_fortezza && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
515 continue;
516 /* now see if this one has more votes than the last best one */
517 if (cipher_votes[mapi] >= max) {
518 /* if equal number of votes, prefer the ones further down in the list */
519 /* with the expectation that these are higher rated ciphers */
520 chosen_cipher = smime_cipher_map[mapi].cipher;
521 max = cipher_votes[mapi];
522 }
523 }
524 /* if no common cipher was found, chosen_cipher stays at the default */
525
526 done:
527 if (poolp != NULL)
528 PORT_FreeArena (poolp, PR_FALSE);
529
530 if (smime_keysize_by_cipher(chosen_cipher) < 128) {
531 /* you're going to use strong(er) crypto whether you like it or not */
532 chosen_cipher = SMIME_DES_EDE3_168;
533 }
534 return chosen_cipher;
535 }
536
537 /*
538 * XXX This is a hack for now to satisfy our current interface.
539 * Eventually, with more parameters needing to be specified, just
540 * looking up the keysize is not going to be sufficient.
541 */
542 static int
543 smime_keysize_by_cipher (unsigned long which)
544 {
545 int keysize;
546
547 switch (which) {
548 case SMIME_RC2_CBC_40:
549 keysize = 40;
550 break;
551 case SMIME_RC2_CBC_64:
552 keysize = 64;
553 break;
554 case SMIME_RC2_CBC_128:
555 keysize = 128;
556 break;
557 case SMIME_DES_CBC_56:
558 keysize = 64;
559 break;
560 case SMIME_DES_EDE3_168:
561 keysize = 192;
562 break;
563 case SMIME_FORTEZZA:
564 /*
565 * This is special; since the key size is fixed, we actually
566 * want to *avoid* specifying a key size.
567 */
568 keysize = 0;
569 break;
570 default:
571 keysize = -1;
572 break;
573 }
574
575 return keysize;
576 }
577
578 /*
579 * SecSMIMEFindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
580 *
581 * it would be great for UI purposes if there would be a way to find out which recipients
582 * prevented a strong cipher from being used...
583 */
584 OSStatus
585 SecSMIMEFindBulkAlgForRecipients(SecCertificateRef *rcerts, SECOidTag *bulkalgtag, int *keysize)
586 {
587 unsigned long cipher;
588 int mapi;
589
590 cipher = smime_choose_cipher(NULL, rcerts);
591 mapi = smime_mapi_by_cipher(cipher);
592
593 *bulkalgtag = smime_cipher_map[mapi].algtag;
594 *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
595
596 return SECSuccess;
597 }
598
599 /*
600 * SecSMIMECreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
601 *
602 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
603 * S/MIME capabilities attribute value.
604 *
605 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
606 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
607 *
608 * "poolp" - arena pool to create the S/MIME capabilities data on
609 * "dest" - SecAsn1Item to put the data in
610 * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
611 */
612 OSStatus
613 SecSMIMECreateSMIMECapabilities(PLArenaPool *poolp, SecAsn1Item *dest, Boolean includeFortezzaCiphers)
614 {
615 NSSSMIMECapability *cap;
616 NSSSMIMECapability **smime_capabilities;
617 smime_cipher_map_entry *map;
618 SECOidData *oiddata;
619 SecAsn1Item *dummy;
620 int i, capIndex;
621
622 /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
623 /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
624 smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
625 * sizeof(NSSSMIMECapability *));
626 if (smime_capabilities == NULL)
627 return SECFailure;
628
629 capIndex = 0;
630
631 /* Add all the symmetric ciphers
632 * We walk the cipher list backwards, as it is ordered by increasing strength,
633 * we prefer the stronger cipher over a weaker one, and we have to list the
634 * preferred algorithm first */
635 for (i = smime_cipher_map_count - 1; i >= 0; i--) {
636 /* Find the corresponding entry in the cipher map. */
637 map = &(smime_cipher_map[i]);
638 if (!map->enabled)
639 continue;
640
641 /* If we're using a non-Fortezza cert, only advertise non-Fortezza
642 capabilities. (We advertise all capabilities if we have a
643 Fortezza cert.) */
644 if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA))
645 continue;
646
647 /* get next SMIME capability */
648 cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
649 if (cap == NULL)
650 break;
651 smime_capabilities[capIndex++] = cap;
652
653 oiddata = SECOID_FindOIDByTag(map->algtag);
654 if (oiddata == NULL)
655 break;
656
657 cap->capabilityID.Data = oiddata->oid.Data;
658 cap->capabilityID.Length = oiddata->oid.Length;
659 cap->parameters.Data = map->parms ? map->parms->Data : NULL;
660 cap->parameters.Length = map->parms ? map->parms->Length : 0;
661 cap->cipher = smime_cipher_map[i].cipher;
662 }
663
664 /* XXX add signature algorithms */
665 /* XXX add key encipherment algorithms */
666
667 smime_capabilities[capIndex] = NULL; /* last one - now encode */
668 dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
669
670 /* now that we have the proper encoded SMIMECapabilities (or not),
671 * free the work data */
672 for (i = 0; smime_capabilities[i] != NULL; i++)
673 PORT_Free(smime_capabilities[i]);
674 PORT_Free(smime_capabilities);
675
676 return (dummy == NULL) ? SECFailure : SECSuccess;
677 }
678
679 /*
680 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
681 *
682 * "poolp" - arena pool to create the attr value on
683 * "dest" - SecAsn1Item to put the data in
684 * "cert" - certificate that should be marked as preferred encryption key
685 * cert is expected to have been verified for EmailRecipient usage.
686 */
687 OSStatus
688 SecSMIMECreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SecAsn1Item *dest, SecCertificateRef cert)
689 {
690 NSSSMIMEEncryptionKeyPreference ekp;
691 SecAsn1Item *dummy = NULL;
692 PLArenaPool *tmppoolp = NULL;
693
694 if (cert == NULL)
695 goto loser;
696
697 tmppoolp = PORT_NewArena(1024);
698 if (tmppoolp == NULL)
699 goto loser;
700
701 /* XXX hardcoded IssuerSN choice for now */
702 ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
703 ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
704 if (ekp.id.issuerAndSN == NULL)
705 goto loser;
706
707 dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
708
709 loser:
710 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
711
712 return (dummy == NULL) ? SECFailure : SECSuccess;
713 }
714
715 /*
716 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
717 *
718 * "poolp" - arena pool to create the attr value on
719 * "dest" - SecAsn1Item to put the data in
720 * "cert" - certificate that should be marked as preferred encryption key
721 * cert is expected to have been verified for EmailRecipient usage.
722 */
723 OSStatus
724 SecSMIMECreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SecAsn1Item *dest, SecCertificateRef cert)
725 {
726 SecAsn1Item *dummy = NULL;
727 PLArenaPool *tmppoolp = NULL;
728 SecCmsIssuerAndSN *isn;
729
730 if (cert == NULL)
731 goto loser;
732
733 tmppoolp = PORT_NewArena(1024);
734 if (tmppoolp == NULL)
735 goto loser;
736
737 isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
738 if (isn == NULL)
739 goto loser;
740
741 dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(SecCmsIssuerAndSNTemplate));
742
743 loser:
744 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
745
746 return (dummy == NULL) ? SECFailure : SECSuccess;
747 }
748
749 #if 0
750 /*
751 * SecSMIMEGetCertFromEncryptionKeyPreference -
752 * find cert marked by EncryptionKeyPreference attribute
753 *
754 * "keychainOrArray" - handle for the cert database to look in
755 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
756 *
757 * if certificate is supposed to be found among the message's included certificates,
758 * they are assumed to have been imported already.
759 */
760 SecCertificateRef
761 SecSMIMEGetCertFromEncryptionKeyPreference(SecKeychainRef keychainOrArray, SecAsn1Item *DERekp)
762 {
763 PLArenaPool *tmppoolp = NULL;
764 SecCertificateRef cert = NULL;
765 NSSSMIMEEncryptionKeyPreference ekp;
766
767 tmppoolp = PORT_NewArena(1024);
768 if (tmppoolp == NULL)
769 return NULL;
770
771 /* decode DERekp */
772 if (SEC_ASN1DecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template, DERekp) != SECSuccess)
773 goto loser;
774
775 /* find cert */
776 switch (ekp.selector) {
777 case NSSSMIMEEncryptionKeyPref_IssuerSN:
778 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, ekp.id.issuerAndSN);
779 break;
780 case NSSSMIMEEncryptionKeyPref_RKeyID:
781 case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
782 /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
783 break;
784 default:
785 PORT_Assert(0);
786 }
787 loser:
788 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
789
790 return cert;
791 }
792 #endif
793
794 #if 0
795 extern const char __nss_smime_rcsid[];
796 extern const char __nss_smime_sccsid[];
797
798 Boolean
799 NSSSMIME_VersionCheck(const char *importedVersion)
800 {
801 #if 1
802 return PR_TRUE;
803 #else
804 /*
805 * This is the secret handshake algorithm.
806 *
807 * This release has a simple version compatibility
808 * check algorithm. This release is not backward
809 * compatible with previous major releases. It is
810 * not compatible with future major, minor, or
811 * patch releases.
812 */
813 volatile char c; /* force a reference that won't get optimized away */
814
815 c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0];
816
817 return NSS_VersionCheck(importedVersion);
818 #endif
819 }
820 #endif /* 0 */
821