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