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