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