]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/smimeutil.c
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / 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 "secitem.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 CSSM_DATA param_int40 = { sizeof(asn1_int40), asn1_int40 };
61 static CSSM_DATA param_int64 = { sizeof(asn1_int64), asn1_int64 };
62 static CSSM_DATA param_int128 = { sizeof(asn1_int128), asn1_int128 };
63
64 /*
65 * XXX Would like the "parameters" field to be a CSSM_DATA_PTR , 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 CSSM_DATA capabilityID;
71 CSSM_DATA 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 CSSM_DATA_PTR 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 CSSM_DATA_PTR 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_TRUE },
143 { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_FALSE,PR_TRUE },
144 { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_FALSE,PR_TRUE },
145 { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
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_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL, PR_FALSE, PR_TRUE }
149 };
150 static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
151
152 /*
153 * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
154 */
155 static int
156 smime_mapi_by_cipher(unsigned long cipher)
157 {
158 int i;
159
160 for (i = 0; i < smime_cipher_map_count; i++) {
161 if (smime_cipher_map[i].cipher == cipher)
162 return i; /* bingo */
163 }
164 return -1; /* should not happen if we're consistent, right? */
165 }
166
167 /*
168 * NSS_SMIME_EnableCipher - this function locally records the user's preference
169 */
170 OSStatus
171 SecSMIMEEnableCipher(uint32 which, Boolean on)
172 {
173 unsigned long mask;
174 int mapi;
175
176 mask = which & CIPHER_FAMILYID_MASK;
177
178 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
179 if (mask != CIPHER_FAMILYID_SMIME)
180 /* XXX set an error! */
181 return SECFailure;
182
183 mapi = smime_mapi_by_cipher(which);
184 if (mapi < 0)
185 /* XXX set an error */
186 return SECFailure;
187
188 /* do we try to turn on a forbidden cipher? */
189 if (!smime_cipher_map[mapi].allowed && on) {
190 PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
191 return SECFailure;
192 }
193
194 smime_cipher_map[mapi].enabled = on;
195
196 return SECSuccess;
197 }
198
199
200 /*
201 * this function locally records the export policy
202 */
203 OSStatus
204 SecSMIMEAllowCipher(uint32 which, Boolean on)
205 {
206 unsigned long mask;
207 int mapi;
208
209 mask = which & CIPHER_FAMILYID_MASK;
210
211 PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
212 if (mask != CIPHER_FAMILYID_SMIME)
213 /* XXX set an error! */
214 return SECFailure;
215
216 mapi = smime_mapi_by_cipher(which);
217 if (mapi < 0)
218 /* XXX set an error */
219 return SECFailure;
220
221 smime_cipher_map[mapi].allowed = on;
222
223 return SECSuccess;
224 }
225
226 /*
227 * Based on the given algorithm (including its parameters, in some cases!)
228 * and the given key (may or may not be inspected, depending on the
229 * algorithm), find the appropriate policy algorithm specification
230 * and return it. If no match can be made, -1 is returned.
231 */
232 static OSStatus
233 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, SecSymmetricKeyRef key, unsigned long *cipher)
234 {
235 SECOidTag algtag;
236 unsigned int keylen_bits;
237 unsigned long c;
238
239 algtag = SECOID_GetAlgorithmTag(algid);
240 switch (algtag) {
241 case SEC_OID_RC2_CBC:
242 if (SecKeyGetStrengthInBits(key, algid, &keylen_bits))
243 return SECFailure;
244 switch (keylen_bits) {
245 case 40:
246 c = SMIME_RC2_CBC_40;
247 break;
248 case 64:
249 c = SMIME_RC2_CBC_64;
250 break;
251 case 128:
252 c = SMIME_RC2_CBC_128;
253 break;
254 default:
255 return SECFailure;
256 }
257 break;
258 case SEC_OID_DES_CBC:
259 c = SMIME_DES_CBC_56;
260 break;
261 case SEC_OID_DES_EDE3_CBC:
262 c = SMIME_DES_EDE3_168;
263 break;
264 case SEC_OID_AES_128_CBC:
265 c = SMIME_AES_CBC_128;
266 break;
267 case SEC_OID_FORTEZZA_SKIPJACK:
268 c = SMIME_FORTEZZA;
269 break;
270 default:
271 return SECFailure;
272 }
273 *cipher = c;
274 return SECSuccess;
275 }
276
277 static Boolean
278 nss_smime_cipher_allowed(unsigned long which)
279 {
280 int mapi;
281
282 mapi = smime_mapi_by_cipher(which);
283 if (mapi < 0)
284 return PR_FALSE;
285 return smime_cipher_map[mapi].allowed;
286 }
287
288 Boolean
289 SecSMIMEDecryptionAllowed(SECAlgorithmID *algid, SecSymmetricKeyRef key)
290 {
291 unsigned long which;
292
293 if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
294 return PR_FALSE;
295
296 return nss_smime_cipher_allowed(which);
297 }
298
299
300 /*
301 * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
302 *
303 * This tells whether or not *any* S/MIME encryption can be done,
304 * according to policy. Callers may use this to do nicer user interface
305 * (say, greying out a checkbox so a user does not even try to encrypt
306 * a message when they are not allowed to) or for any reason they want
307 * to check whether S/MIME encryption (or decryption, for that matter)
308 * may be done.
309 *
310 * It takes no arguments. The return value is a simple boolean:
311 * PR_TRUE means encryption (or decryption) is *possible*
312 * (but may still fail due to other reasons, like because we cannot
313 * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
314 * PR_FALSE means encryption (or decryption) is not permitted
315 *
316 * There are no errors from this routine.
317 */
318 Boolean
319 SecSMIMEEncryptionPossible(void)
320 {
321 int i;
322
323 for (i = 0; i < smime_cipher_map_count; i++) {
324 if (smime_cipher_map[i].allowed)
325 return PR_TRUE;
326 }
327 return PR_FALSE;
328 }
329
330
331 static unsigned long
332 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
333 {
334 int i;
335 SECOidTag capIDTag;
336
337 /* we need the OIDTag here */
338 capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
339
340 /* go over all the SMIME ciphers we know and see if we find a match */
341 for (i = 0; i < smime_cipher_map_count; i++) {
342 if (smime_cipher_map[i].algtag != capIDTag)
343 continue;
344 /*
345 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
346 * 2 NULLs as equal and NULL and non-NULL as not equal), we could
347 * use that here instead of all of the following comparison code.
348 */
349 if (cap->parameters.Data == NULL && smime_cipher_map[i].parms == NULL)
350 break; /* both empty: bingo */
351
352 if (cap->parameters.Data != NULL && smime_cipher_map[i].parms != NULL &&
353 cap->parameters.Length == smime_cipher_map[i].parms->Length &&
354 PORT_Memcmp (cap->parameters.Data, smime_cipher_map[i].parms->Data,
355 cap->parameters.Length) == 0)
356 {
357 break; /* both not empty, same length & equal content: bingo */
358 }
359 }
360
361 if (i == smime_cipher_map_count)
362 return 0; /* no match found */
363 else
364 return smime_cipher_map[i].cipher; /* match found, point to cipher */
365 }
366
367 static int smime_keysize_by_cipher (unsigned long which);
368
369 /*
370 * smime_choose_cipher - choose a cipher that works for all the recipients
371 *
372 * "scert" - sender's certificate
373 * "rcerts" - recipient's certificates
374 */
375 static long
376 smime_choose_cipher(SecCertificateRef scert, SecCertificateRef *rcerts)
377 {
378 PRArenaPool *poolp;
379 long cipher;
380 long chosen_cipher = SMIME_DES_EDE3_168;
381 int *cipher_abilities;
382 int *cipher_votes;
383 int strong_mapi;
384 int rcount, mapi, max, i;
385 #if 1
386 // @@@ We Don't support Fortezza yet.
387 Boolean scert_is_fortezza = PR_FALSE;
388 #else
389 Boolean scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
390 #endif
391
392 poolp = PORT_NewArena (1024); /* XXX what is right value? */
393 if (poolp == NULL)
394 goto done;
395
396 cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
397 cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
398 if (cipher_votes == NULL || cipher_abilities == NULL)
399 goto done;
400
401 /* If the user has the Fortezza preference turned on, make
402 * that the strong cipher. Otherwise, use triple-DES. */
403 strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
404 if (scert_is_fortezza) {
405 mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
406 if (mapi >= 0 && smime_cipher_map[mapi].enabled)
407 strong_mapi = mapi;
408 }
409
410 /* walk all the recipient's certs */
411 for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
412 CSSM_DATA_PTR profile;
413 NSSSMIMECapability **caps;
414 int pref;
415
416 /* the first cipher that matches in the user's SMIME profile gets
417 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
418 * and so on. If every cipher matches, the last one gets 1 (one) vote */
419 pref = smime_cipher_map_count;
420
421 /* find recipient's SMIME profile */
422 profile = CERT_FindSMimeProfile(rcerts[rcount]);
423
424 if (profile != NULL && profile->Data != NULL && profile->Length > 0) {
425 /* we have a profile (still DER-encoded) */
426 caps = NULL;
427 /* decode it */
428 if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
429 caps != NULL)
430 {
431 /* walk the SMIME capabilities for this recipient */
432 for (i = 0; caps[i] != NULL; i++) {
433 cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
434 mapi = smime_mapi_by_cipher(cipher);
435 if (mapi >= 0) {
436 /* found the cipher */
437 cipher_abilities[mapi]++;
438 cipher_votes[mapi] += pref;
439 --pref;
440 }
441 }
442 }
443 } else {
444 /* cast votes for the strong algorithm */
445 cipher_abilities[strong_mapi]++;
446 cipher_votes[strong_mapi] += pref;
447 pref--;
448 }
449 if (profile != NULL)
450 SECITEM_FreeItem(profile, PR_TRUE);
451 }
452
453 /* find cipher that is agreeable by all recipients and that has the most votes */
454 max = 0;
455 for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
456 /* if not all of the recipients can do this, forget it */
457 if (cipher_abilities[mapi] != rcount)
458 continue;
459 /* if cipher is not enabled or not allowed by policy, forget it */
460 if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
461 continue;
462 /* if we're not doing fortezza, but the cipher is fortezza, forget it */
463 if (!scert_is_fortezza && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
464 continue;
465 /* now see if this one has more votes than the last best one */
466 if (cipher_votes[mapi] >= max) {
467 /* if equal number of votes, prefer the ones further down in the list */
468 /* with the expectation that these are higher rated ciphers */
469 chosen_cipher = smime_cipher_map[mapi].cipher;
470 max = cipher_votes[mapi];
471 }
472 }
473 /* if no common cipher was found, chosen_cipher stays at the default */
474
475 done:
476 if (poolp != NULL)
477 PORT_FreeArena (poolp, PR_FALSE);
478
479 if (smime_keysize_by_cipher(chosen_cipher) < 128) {
480 /* you're going to use strong(er) crypto whether you like it or not */
481 chosen_cipher = SMIME_DES_EDE3_168;
482 }
483 return chosen_cipher;
484 }
485
486 /*
487 * XXX This is a hack for now to satisfy our current interface.
488 * Eventually, with more parameters needing to be specified, just
489 * looking up the keysize is not going to be sufficient.
490 */
491 static int
492 smime_keysize_by_cipher (unsigned long which)
493 {
494 int keysize;
495
496 switch (which) {
497 case SMIME_RC2_CBC_40:
498 keysize = 40;
499 break;
500 case SMIME_RC2_CBC_64:
501 keysize = 64;
502 break;
503 case SMIME_RC2_CBC_128:
504 case SMIME_AES_CBC_128:
505 keysize = 128;
506 break;
507 case SMIME_DES_CBC_56:
508 keysize = 64;
509 break;
510 case SMIME_DES_EDE3_168:
511 keysize = 192;
512 break;
513 case SMIME_FORTEZZA:
514 /*
515 * This is special; since the key size is fixed, we actually
516 * want to *avoid* specifying a key size.
517 */
518 keysize = 0;
519 break;
520 default:
521 keysize = -1;
522 break;
523 }
524
525 return keysize;
526 }
527
528 /*
529 * SecSMIMEFindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
530 *
531 * it would be great for UI purposes if there would be a way to find out which recipients
532 * prevented a strong cipher from being used...
533 */
534 OSStatus
535 SecSMIMEFindBulkAlgForRecipients(SecCertificateRef *rcerts, SECOidTag *bulkalgtag, int *keysize)
536 {
537 unsigned long cipher;
538 int mapi;
539
540 cipher = smime_choose_cipher(NULL, rcerts);
541 mapi = smime_mapi_by_cipher(cipher);
542 if (mapi < 0) {
543 return SECFailure;
544 }
545
546 *bulkalgtag = smime_cipher_map[mapi].algtag;
547 *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
548
549 return SECSuccess;
550 }
551
552 /*
553 * SecSMIMECreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
554 *
555 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
556 * S/MIME capabilities attribute value.
557 *
558 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
559 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
560 *
561 * "poolp" - arena pool to create the S/MIME capabilities data on
562 * "dest" - CSSM_DATA to put the data in
563 * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
564 */
565 OSStatus
566 SecSMIMECreateSMIMECapabilities(SecArenaPoolRef pool, CSSM_DATA_PTR dest, Boolean includeFortezzaCiphers)
567 {
568 PLArenaPool *poolp = (PLArenaPool *)pool;
569 NSSSMIMECapability *cap;
570 NSSSMIMECapability **smime_capabilities;
571 smime_cipher_map_entry *map;
572 SECOidData *oiddata;
573 CSSM_DATA_PTR dummy;
574 int i, capIndex;
575
576 /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
577 /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
578 smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
579 * sizeof(NSSSMIMECapability *));
580 if (smime_capabilities == NULL)
581 return SECFailure;
582
583 capIndex = 0;
584
585 /* Add all the symmetric ciphers
586 * We walk the cipher list backwards, as it is ordered by increasing strength,
587 * we prefer the stronger cipher over a weaker one, and we have to list the
588 * preferred algorithm first */
589 for (i = smime_cipher_map_count - 1; i >= 0; i--) {
590 /* Find the corresponding entry in the cipher map. */
591 map = &(smime_cipher_map[i]);
592 if (!map->enabled)
593 continue;
594
595 /* If we're using a non-Fortezza cert, only advertise non-Fortezza
596 capabilities. (We advertise all capabilities if we have a
597 Fortezza cert.) */
598 if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA))
599 continue;
600
601 /* get next SMIME capability */
602 cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
603 if (cap == NULL)
604 break;
605 smime_capabilities[capIndex++] = cap;
606
607 oiddata = SECOID_FindOIDByTag(map->algtag);
608 if (oiddata == NULL)
609 break;
610
611 cap->capabilityID.Data = oiddata->oid.Data;
612 cap->capabilityID.Length = oiddata->oid.Length;
613 cap->parameters.Data = map->parms ? map->parms->Data : NULL;
614 cap->parameters.Length = map->parms ? map->parms->Length : 0;
615 cap->cipher = smime_cipher_map[i].cipher;
616 }
617
618 /* XXX add signature algorithms */
619 /* XXX add key encipherment algorithms */
620
621 smime_capabilities[capIndex] = NULL; /* last one - now encode */
622 dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
623
624 /* now that we have the proper encoded SMIMECapabilities (or not),
625 * free the work data */
626 for (i = 0; smime_capabilities[i] != NULL; i++)
627 PORT_Free(smime_capabilities[i]);
628 PORT_Free(smime_capabilities);
629
630 return (dummy == NULL) ? SECFailure : SECSuccess;
631 }
632
633 /*
634 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
635 *
636 * "poolp" - arena pool to create the attr value on
637 * "dest" - CSSM_DATA to put the data in
638 * "cert" - certificate that should be marked as preferred encryption key
639 * cert is expected to have been verified for EmailRecipient usage.
640 */
641 OSStatus
642 SecSMIMECreateSMIMEEncKeyPrefs(SecArenaPoolRef pool, CSSM_DATA_PTR dest, SecCertificateRef cert)
643 {
644 PLArenaPool *poolp = (PLArenaPool *)pool;
645 NSSSMIMEEncryptionKeyPreference ekp;
646 CSSM_DATA_PTR dummy = NULL;
647 PLArenaPool *tmppoolp = NULL;
648
649 if (cert == NULL)
650 goto loser;
651
652 tmppoolp = PORT_NewArena(1024);
653 if (tmppoolp == NULL)
654 goto loser;
655
656 /* XXX hardcoded IssuerSN choice for now */
657 ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
658 ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
659 if (ekp.id.issuerAndSN == NULL)
660 goto loser;
661
662 dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
663
664 loser:
665 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
666
667 return (dummy == NULL) ? SECFailure : SECSuccess;
668 }
669
670 /*
671 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
672 *
673 * "poolp" - arena pool to create the attr value on
674 * "dest" - CSSM_DATA to put the data in
675 * "cert" - certificate that should be marked as preferred encryption key
676 * cert is expected to have been verified for EmailRecipient usage.
677 */
678 OSStatus
679 SecSMIMECreateMSSMIMEEncKeyPrefs(SecArenaPoolRef pool, CSSM_DATA_PTR dest, SecCertificateRef cert)
680 {
681 PLArenaPool *poolp = (PLArenaPool *)pool;
682 CSSM_DATA_PTR dummy = NULL;
683 PLArenaPool *tmppoolp = NULL;
684 SecCmsIssuerAndSN *isn;
685
686 if (cert == NULL)
687 goto loser;
688
689 tmppoolp = PORT_NewArena(1024);
690 if (tmppoolp == NULL)
691 goto loser;
692
693 isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
694 if (isn == NULL)
695 goto loser;
696
697 dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(SecCmsIssuerAndSNTemplate));
698
699 loser:
700 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
701
702 return (dummy == NULL) ? SECFailure : SECSuccess;
703 }
704
705 /*
706 * SecSMIMEGetCertFromEncryptionKeyPreference -
707 * find cert marked by EncryptionKeyPreference attribute
708 *
709 * "keychainOrArray" - handle for the cert database to look in
710 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
711 *
712 * if certificate is supposed to be found among the message's included certificates,
713 * they are assumed to have been imported already.
714 */
715 SecCertificateRef
716 SecSMIMEGetCertFromEncryptionKeyPreference(SecKeychainRef keychainOrArray, CSSM_DATA_PTR *rawCerts, CSSM_DATA_PTR DERekp)
717 {
718 PLArenaPool *tmppoolp = NULL;
719 SecCertificateRef cert = NULL;
720 NSSSMIMEEncryptionKeyPreference ekp;
721
722 tmppoolp = PORT_NewArena(1024);
723 if (tmppoolp == NULL)
724 return NULL;
725
726 /* decode DERekp */
727 if (SEC_ASN1DecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template, DERekp) != SECSuccess)
728 goto loser;
729
730 /* find cert */
731 switch (ekp.selector) {
732 case NSSSMIMEEncryptionKeyPref_IssuerSN:
733 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, NULL, tmppoolp, ekp.id.issuerAndSN);
734 break;
735 case NSSSMIMEEncryptionKeyPref_RKeyID:
736 case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
737 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, NULL, ekp.id.subjectKeyID);
738 break;
739 default:
740 PORT_Assert(0);
741 }
742 loser:
743 if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
744
745 return cert;
746 }
747
748 #if 0
749 extern const char __nss_smime_rcsid[];
750 extern const char __nss_smime_sccsid[];
751 #endif
752
753 #if 0 /* -- unused */
754 Boolean
755 NSSSMIME_VersionCheck(const char *importedVersion)
756 {
757 #if 1
758 return PR_TRUE;
759 #else
760 /*
761 * This is the secret handshake algorithm.
762 *
763 * This release has a simple version compatibility
764 * check algorithm. This release is not backward
765 * compatible with previous major releases. It is
766 * not compatible with future major, minor, or
767 * patch releases.
768 */
769 volatile char c; /* force a reference that won't get optimized away */
770
771 c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0];
772
773 return NSS_VersionCheck(importedVersion);
774 #endif
775 }
776 #endif