secmime.c (24893B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * Stuff specific to S/MIME policy and interoperability. 7 * Depends on PKCS7, but there should be no dependency the other way around. 8 */ 9 10 #include "secmime.h" 11 #include "secoid.h" 12 #include "pk11func.h" 13 #include "ciferfam.h" /* for CIPHER_FAMILY symbols */ 14 #include "secasn1.h" 15 #include "secitem.h" 16 #include "cert.h" 17 #include "keyhi.h" 18 #include "secerr.h" 19 20 typedef struct smime_cipher_map_struct { 21 unsigned long cipher; 22 SECOidTag algtag; 23 SECItem *parms; 24 } smime_cipher_map; 25 26 /* 27 * These are macros because I think some subsequent parameters, 28 * like those for RC5, will want to use them, too, separately. 29 */ 30 #define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10 31 #define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28 32 #define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40 33 #define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 34 35 #ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */ 36 static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 }; 37 #endif 38 static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 }; 39 static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 }; 40 static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 }; 41 42 static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) }; 43 static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) }; 44 static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) }; 45 46 static smime_cipher_map smime_cipher_maps[] = { 47 { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 }, 48 { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 }, 49 { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 }, 50 #ifdef SMIME_DOES_RC5 51 { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 }, 52 { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 }, 53 { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 }, 54 #endif 55 { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL }, 56 { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL } 57 }; 58 59 /* 60 * Note, the following value really just needs to be an upper bound 61 * on the ciphers. 62 */ 63 static const int smime_symmetric_count = sizeof(smime_cipher_maps) / sizeof(smime_cipher_map); 64 65 static unsigned long *smime_prefs, *smime_newprefs; 66 static int smime_current_pref_index = 0; 67 static PRBool smime_prefs_complete = PR_FALSE; 68 static PRBool smime_prefs_changed = PR_TRUE; 69 70 static unsigned long smime_policy_bits = 0; 71 72 static int 73 smime_mapi_by_cipher(unsigned long cipher) 74 { 75 int i; 76 77 for (i = 0; i < smime_symmetric_count; i++) { 78 if (smime_cipher_maps[i].cipher == cipher) 79 break; 80 } 81 82 if (i == smime_symmetric_count) 83 return -1; 84 85 return i; 86 } 87 88 /* 89 * this function locally records the user's preference 90 */ 91 SECStatus 92 SECMIME_EnableCipher(long which, int on) 93 { 94 unsigned long mask; 95 96 if (smime_newprefs == NULL || smime_prefs_complete) { 97 /* 98 * This is either the very first time, or we are starting over. 99 */ 100 smime_newprefs = (unsigned long *)PORT_ZAlloc(smime_symmetric_count * sizeof(*smime_newprefs)); 101 if (smime_newprefs == NULL) 102 return SECFailure; 103 smime_current_pref_index = 0; 104 smime_prefs_complete = PR_FALSE; 105 } 106 107 mask = which & CIPHER_FAMILYID_MASK; 108 if (mask == CIPHER_FAMILYID_MASK) { 109 /* 110 * This call signifies that all preferences have been set. 111 * Move "newprefs" over, after checking first whether or 112 * not the new ones are different from the old ones. 113 */ 114 if (smime_prefs != NULL) { 115 if (PORT_Memcmp(smime_prefs, smime_newprefs, 116 smime_symmetric_count * sizeof(*smime_prefs)) == 0) 117 smime_prefs_changed = PR_FALSE; 118 else 119 smime_prefs_changed = PR_TRUE; 120 PORT_Free(smime_prefs); 121 } 122 123 smime_prefs = smime_newprefs; 124 smime_prefs_complete = PR_TRUE; 125 return SECSuccess; 126 } 127 128 PORT_Assert(mask == CIPHER_FAMILYID_SMIME); 129 if (mask != CIPHER_FAMILYID_SMIME) { 130 /* XXX set an error! */ 131 return SECFailure; 132 } 133 134 if (on) { 135 PORT_Assert(smime_current_pref_index < smime_symmetric_count); 136 if (smime_current_pref_index >= smime_symmetric_count) { 137 /* XXX set an error! */ 138 return SECFailure; 139 } 140 141 smime_newprefs[smime_current_pref_index++] = which; 142 } 143 144 return SECSuccess; 145 } 146 147 /* 148 * this function locally records the export policy 149 */ 150 SECStatus 151 SECMIME_SetPolicy(long which, int on) 152 { 153 unsigned long mask; 154 155 PORT_Assert((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME); 156 if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) { 157 /* XXX set an error! */ 158 return SECFailure; 159 } 160 161 which &= ~CIPHER_FAMILYID_MASK; 162 163 PORT_Assert(which < 32); /* bits in the long */ 164 if (which >= 32) { 165 /* XXX set an error! */ 166 return SECFailure; 167 } 168 169 mask = 1UL << which; 170 171 if (on) { 172 smime_policy_bits |= mask; 173 } else { 174 smime_policy_bits &= ~mask; 175 } 176 177 return SECSuccess; 178 } 179 180 /* 181 * Based on the given algorithm (including its parameters, in some cases!) 182 * and the given key (may or may not be inspected, depending on the 183 * algorithm), find the appropriate policy algorithm specification 184 * and return it. If no match can be made, -1 is returned. 185 */ 186 static long 187 smime_policy_algorithm(SECAlgorithmID *algid, PK11SymKey *key) 188 { 189 SECOidTag algtag; 190 191 algtag = SECOID_GetAlgorithmTag(algid); 192 switch (algtag) { 193 case SEC_OID_RC2_CBC: { 194 unsigned int keylen_bits; 195 196 keylen_bits = PK11_GetKeyStrength(key, algid); 197 switch (keylen_bits) { 198 case 40: 199 return SMIME_RC2_CBC_40; 200 case 64: 201 return SMIME_RC2_CBC_64; 202 case 128: 203 return SMIME_RC2_CBC_128; 204 default: 205 break; 206 } 207 } break; 208 case SEC_OID_DES_CBC: 209 return SMIME_DES_CBC_56; 210 case SEC_OID_DES_EDE3_CBC: 211 return SMIME_DES_EDE3_168; 212 #ifdef SMIME_DOES_RC5 213 case SEC_OID_RC5_CBC_PAD: 214 PORT_Assert(0); /* XXX need to pull out parameters and match */ 215 break; 216 #endif 217 default: 218 break; 219 } 220 221 return -1; 222 } 223 224 static PRBool 225 smime_cipher_allowed(unsigned long which) 226 { 227 unsigned long mask; 228 229 which &= ~CIPHER_FAMILYID_MASK; 230 PORT_Assert(which < 32); /* bits per long (min) */ 231 if (which >= 32) 232 return PR_FALSE; 233 234 mask = 1UL << which; 235 if ((mask & smime_policy_bits) == 0) 236 return PR_FALSE; 237 238 return PR_TRUE; 239 } 240 241 PRBool 242 SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key) 243 { 244 long which; 245 246 which = smime_policy_algorithm(algid, key); 247 if (which < 0) 248 return PR_FALSE; 249 250 return smime_cipher_allowed((unsigned long)which); 251 } 252 253 /* 254 * Does the current policy allow *any* S/MIME encryption (or decryption)? 255 * 256 * This tells whether or not *any* S/MIME encryption can be done, 257 * according to policy. Callers may use this to do nicer user interface 258 * (say, greying out a checkbox so a user does not even try to encrypt 259 * a message when they are not allowed to) or for any reason they want 260 * to check whether S/MIME encryption (or decryption, for that matter) 261 * may be done. 262 * 263 * It takes no arguments. The return value is a simple boolean: 264 * PR_TRUE means encryption (or decryption) is *possible* 265 * (but may still fail due to other reasons, like because we cannot 266 * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) 267 * PR_FALSE means encryption (or decryption) is not permitted 268 * 269 * There are no errors from this routine. 270 */ 271 PRBool 272 SECMIME_EncryptionPossible(void) 273 { 274 if (smime_policy_bits != 0) 275 return PR_TRUE; 276 277 return PR_FALSE; 278 } 279 280 /* 281 * XXX Would like the "parameters" field to be a SECItem *, but the 282 * encoder is having trouble with optional pointers to an ANY. Maybe 283 * once that is fixed, can change this back... 284 */ 285 typedef struct smime_capability_struct { 286 unsigned long cipher; /* local; not part of encoding */ 287 SECOidTag capIDTag; /* local; not part of encoding */ 288 SECItem capabilityID; 289 SECItem parameters; 290 } smime_capability; 291 292 static const SEC_ASN1Template smime_capability_template[] = { 293 { SEC_ASN1_SEQUENCE, 294 0, NULL, sizeof(smime_capability) }, 295 { SEC_ASN1_OBJECT_ID, 296 offsetof(smime_capability, capabilityID) }, 297 { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, 298 offsetof(smime_capability, parameters) }, 299 { 0 } 300 }; 301 302 static const SEC_ASN1Template smime_capabilities_template[] = { 303 { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template } 304 }; 305 306 static void 307 smime_fill_capability(smime_capability *cap) 308 { 309 unsigned long cipher; 310 SECOidTag algtag; 311 int i; 312 313 algtag = SECOID_FindOIDTag(&(cap->capabilityID)); 314 315 for (i = 0; i < smime_symmetric_count; i++) { 316 if (smime_cipher_maps[i].algtag != algtag) 317 continue; 318 /* 319 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing 320 * 2 NULLs as equal and NULL and non-NULL as not equal), we could 321 * use that here instead of all of the following comparison code. 322 */ 323 if (cap->parameters.data != NULL) { 324 if (smime_cipher_maps[i].parms == NULL) 325 continue; 326 if (cap->parameters.len != smime_cipher_maps[i].parms->len) 327 continue; 328 if (PORT_Memcmp(cap->parameters.data, 329 smime_cipher_maps[i].parms->data, 330 cap->parameters.len) == 0) 331 break; 332 } else if (smime_cipher_maps[i].parms == NULL) { 333 break; 334 } 335 } 336 337 if (i == smime_symmetric_count) 338 cipher = 0; 339 else 340 cipher = smime_cipher_maps[i].cipher; 341 342 cap->cipher = cipher; 343 cap->capIDTag = algtag; 344 } 345 346 static long 347 smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts) 348 { 349 PLArenaPool *poolp; 350 long chosen_cipher; 351 int *cipher_abilities; 352 int *cipher_votes; 353 int strong_mapi; 354 int rcount, mapi, max; 355 356 if (smime_policy_bits == 0) { 357 PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); 358 return -1; 359 } 360 361 chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ 362 363 poolp = PORT_NewArena(1024); /* XXX what is right value? */ 364 if (poolp == NULL) 365 goto done; 366 367 cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, 368 smime_symmetric_count * sizeof(int)); 369 if (cipher_abilities == NULL) 370 goto done; 371 372 cipher_votes = (int *)PORT_ArenaZAlloc(poolp, 373 smime_symmetric_count * sizeof(int)); 374 if (cipher_votes == NULL) 375 goto done; 376 377 /* 378 * XXX Should have a #define somewhere which specifies default 379 * strong cipher. (Or better, a way to configure.) 380 */ 381 382 /* Make triple-DES the strong cipher. */ 383 strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168); 384 385 PORT_Assert(strong_mapi >= 0); 386 387 for (rcount = 0; rcerts[rcount] != NULL; rcount++) { 388 SECItem *profile; 389 smime_capability **caps; 390 int capi, pref; 391 SECStatus dstat; 392 393 pref = smime_symmetric_count; 394 profile = CERT_FindSMimeProfile(rcerts[rcount]); 395 if (profile != NULL && profile->data != NULL && profile->len > 0) { 396 caps = NULL; 397 dstat = SEC_QuickDERDecodeItem(poolp, &caps, 398 smime_capabilities_template, 399 profile); 400 if (dstat == SECSuccess && caps != NULL) { 401 for (capi = 0; caps[capi] != NULL; capi++) { 402 smime_fill_capability(caps[capi]); 403 mapi = smime_mapi_by_cipher(caps[capi]->cipher); 404 if (mapi >= 0) { 405 cipher_abilities[mapi]++; 406 cipher_votes[mapi] += pref; 407 --pref; 408 } 409 } 410 } 411 } else { 412 SECKEYPublicKey *key; 413 unsigned int pklen_bits; 414 415 /* 416 * XXX This is probably only good for RSA keys. What I would 417 * really like is a function to just say; Is the public key in 418 * this cert an export-length key? Then I would not have to 419 * know things like the value 512, or the kind of key, or what 420 * a subjectPublicKeyInfo is, etc. 421 */ 422 key = CERT_ExtractPublicKey(rcerts[rcount]); 423 if (key != NULL) { 424 pklen_bits = SECKEY_PublicKeyStrength(key) * 8; 425 SECKEY_DestroyPublicKey(key); 426 427 if (pklen_bits > 512) { 428 cipher_abilities[strong_mapi]++; 429 cipher_votes[strong_mapi] += pref; 430 } 431 } 432 } 433 if (profile != NULL) 434 SECITEM_FreeItem(profile, PR_TRUE); 435 } 436 437 max = 0; 438 for (mapi = 0; mapi < smime_symmetric_count; mapi++) { 439 if (cipher_abilities[mapi] != rcount) 440 continue; 441 if (!smime_cipher_allowed(smime_cipher_maps[mapi].cipher)) 442 continue; 443 if (cipher_votes[mapi] > max) { 444 chosen_cipher = smime_cipher_maps[mapi].cipher; 445 max = cipher_votes[mapi]; 446 } /* XXX else if a tie, let scert break it? */ 447 } 448 449 done: 450 if (poolp != NULL) 451 PORT_FreeArena(poolp, PR_FALSE); 452 453 return chosen_cipher; 454 } 455 456 /* 457 * XXX This is a hack for now to satisfy our current interface. 458 * Eventually, with more parameters needing to be specified, just 459 * looking up the keysize is not going to be sufficient. 460 */ 461 static int 462 smime_keysize_by_cipher(unsigned long which) 463 { 464 int keysize; 465 466 switch (which) { 467 case SMIME_RC2_CBC_40: 468 keysize = 40; 469 break; 470 case SMIME_RC2_CBC_64: 471 keysize = 64; 472 break; 473 case SMIME_RC2_CBC_128: 474 keysize = 128; 475 break; 476 #ifdef SMIME_DOES_RC5 477 case SMIME_RC5PAD_64_16_40: 478 case SMIME_RC5PAD_64_16_64: 479 case SMIME_RC5PAD_64_16_128: 480 /* XXX See comment above; keysize is not enough... */ 481 PORT_Assert(0); 482 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 483 keysize = -1; 484 break; 485 #endif 486 case SMIME_DES_CBC_56: 487 case SMIME_DES_EDE3_168: 488 /* 489 * These are special; since the key size is fixed, we actually 490 * want to *avoid* specifying a key size. 491 */ 492 keysize = 0; 493 break; 494 default: 495 keysize = -1; 496 break; 497 } 498 499 return keysize; 500 } 501 502 /* 503 * Start an S/MIME encrypting context. 504 * 505 * "scert" is the cert for the sender. It will be checked for validity. 506 * "rcerts" are the certs for the recipients. They will also be checked. 507 * 508 * "certdb" is the cert database to use for verifying the certs. 509 * It can be NULL if a default database is available (like in the client). 510 * 511 * This function already does all of the stuff specific to S/MIME protocol 512 * and local policy; the return value just needs to be passed to 513 * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, 514 * and finally to SEC_PKCS7DestroyContentInfo(). 515 * 516 * An error results in a return value of NULL and an error set. 517 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 518 */ 519 SEC_PKCS7ContentInfo * 520 SECMIME_CreateEncrypted(CERTCertificate *scert, 521 CERTCertificate **rcerts, 522 CERTCertDBHandle *certdb, 523 SECKEYGetPasswordKey pwfn, 524 void *pwfn_arg) 525 { 526 SEC_PKCS7ContentInfo *cinfo; 527 long cipher; 528 SECOidTag encalg; 529 int keysize; 530 int mapi, rci; 531 532 cipher = smime_choose_cipher(scert, rcerts); 533 if (cipher < 0) 534 return NULL; 535 536 mapi = smime_mapi_by_cipher(cipher); 537 if (mapi < 0) 538 return NULL; 539 540 /* 541 * XXX This is stretching it -- CreateEnvelopedData should probably 542 * take a cipher itself of some sort, because we cannot know what the 543 * future will bring in terms of parameters for each type of algorithm. 544 * For example, just an algorithm and keysize is *not* sufficient to 545 * fully specify the usage of RC5 (which also needs to know rounds and 546 * block size). Work this out into a better API! 547 */ 548 encalg = smime_cipher_maps[mapi].algtag; 549 keysize = smime_keysize_by_cipher(cipher); 550 if (keysize < 0) 551 return NULL; 552 553 cinfo = SEC_PKCS7CreateEnvelopedData(scert, certUsageEmailRecipient, 554 certdb, encalg, keysize, 555 pwfn, pwfn_arg); 556 if (cinfo == NULL) 557 return NULL; 558 559 for (rci = 0; rcerts[rci] != NULL; rci++) { 560 if (rcerts[rci] == scert) 561 continue; 562 if (SEC_PKCS7AddRecipient(cinfo, rcerts[rci], certUsageEmailRecipient, 563 NULL) != SECSuccess) { 564 SEC_PKCS7DestroyContentInfo(cinfo); 565 return NULL; 566 } 567 } 568 569 return cinfo; 570 } 571 572 static smime_capability **smime_capabilities; 573 static SECItem *smime_encoded_caps; 574 575 static SECStatus 576 smime_init_caps(void) 577 { 578 smime_capability *cap; 579 smime_cipher_map *map; 580 SECOidData *oiddata; 581 SECStatus rv; 582 int i; 583 584 if (smime_encoded_caps != NULL && (!smime_prefs_changed)) 585 return SECSuccess; 586 587 if (smime_encoded_caps != NULL) { 588 SECITEM_FreeItem(smime_encoded_caps, PR_TRUE); 589 smime_encoded_caps = NULL; 590 } 591 592 if (smime_capabilities == NULL) { 593 smime_capabilities = (smime_capability **)PORT_ZAlloc( 594 (smime_symmetric_count + 1) * sizeof(smime_capability *)); 595 if (smime_capabilities == NULL) 596 return SECFailure; 597 } 598 599 rv = SECFailure; 600 601 /* 602 The process of creating the encoded PKCS7 cipher capability list 603 involves two basic steps: 604 605 (a) Convert our internal representation of cipher preferences 606 (smime_prefs) into an array containing cipher OIDs and 607 parameter data (smime_capabilities). This step is 608 performed here. 609 610 (b) Encode, using ASN.1, the cipher information in 611 smime_capabilities, leaving the encoded result in 612 smime_encoded_caps. 613 614 (In the process of performing (a), Lisa put in some optimizations 615 which allow us to avoid needlessly re-populating elements in 616 smime_capabilities as we walk through smime_prefs.) 617 */ 618 for (i = 0; i < smime_current_pref_index; i++) { 619 int mapi; 620 621 /* Get the next cipher preference in smime_prefs. */ 622 mapi = smime_mapi_by_cipher(smime_prefs[i]); 623 if (mapi < 0) 624 break; 625 626 /* Find the corresponding entry in the cipher map. */ 627 PORT_Assert(mapi < smime_symmetric_count); 628 map = &(smime_cipher_maps[mapi]); 629 630 /* 631 * Convert the next preference found in smime_prefs into an 632 * smime_capability. 633 */ 634 635 cap = smime_capabilities[i]; 636 if (cap == NULL) { 637 cap = (smime_capability *)PORT_ZAlloc(sizeof(smime_capability)); 638 if (cap == NULL) 639 break; 640 smime_capabilities[i] = cap; 641 } else if (cap->cipher == smime_prefs[i]) { 642 continue; /* no change to this one */ 643 } 644 645 cap->capIDTag = map->algtag; 646 oiddata = SECOID_FindOIDByTag(map->algtag); 647 if (oiddata == NULL) 648 break; 649 650 if (cap->capabilityID.data != NULL) { 651 SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE); 652 cap->capabilityID.data = NULL; 653 cap->capabilityID.len = 0; 654 } 655 656 rv = SECITEM_CopyItem(NULL, &(cap->capabilityID), &(oiddata->oid)); 657 if (rv != SECSuccess) 658 break; 659 660 if (map->parms == NULL) { 661 cap->parameters.data = NULL; 662 cap->parameters.len = 0; 663 } else { 664 cap->parameters.data = map->parms->data; 665 cap->parameters.len = map->parms->len; 666 } 667 668 cap->cipher = smime_prefs[i]; 669 } 670 671 if (i != smime_current_pref_index) 672 return rv; 673 674 while (i < smime_symmetric_count) { 675 cap = smime_capabilities[i]; 676 if (cap != NULL) { 677 SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE); 678 PORT_Free(cap); 679 } 680 smime_capabilities[i] = NULL; 681 i++; 682 } 683 smime_capabilities[i] = NULL; 684 685 smime_encoded_caps = SEC_ASN1EncodeItem(NULL, NULL, &smime_capabilities, 686 smime_capabilities_template); 687 if (smime_encoded_caps == NULL) 688 return SECFailure; 689 690 return SECSuccess; 691 } 692 693 static SECStatus 694 smime_add_profile(CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo) 695 { 696 PORT_Assert(smime_prefs_complete); 697 if (!smime_prefs_complete) 698 return SECFailure; 699 700 /* For that matter, if capabilities haven't been initialized yet, 701 do so now. */ 702 if (smime_encoded_caps == NULL || smime_prefs_changed) { 703 SECStatus rv; 704 705 rv = smime_init_caps(); 706 if (rv != SECSuccess) 707 return rv; 708 709 PORT_Assert(smime_encoded_caps != NULL); 710 } 711 712 return SEC_PKCS7AddSignedAttribute(cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES, 713 smime_encoded_caps); 714 } 715 716 /* 717 * Start an S/MIME signing context. 718 * 719 * "scert" is the cert that will be used to sign the data. It will be 720 * checked for validity. 721 * 722 * "ecert" is the signer's encryption cert. If it is different from 723 * scert, then it will be included in the signed message so that the 724 * recipient can save it for future encryptions. 725 * 726 * "certdb" is the cert database to use for verifying the cert. 727 * It can be NULL if a default database is available (like in the client). 728 * 729 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). 730 * XXX There should be SECMIME functions for hashing, or the hashing should 731 * be built into this interface, which we would like because we would 732 * support more smartcards that way, and then this argument should go away.) 733 * 734 * "digest" is the actual digest of the data. It must be provided in 735 * the case of detached data or NULL if the content will be included. 736 * 737 * This function already does all of the stuff specific to S/MIME protocol 738 * and local policy; the return value just needs to be passed to 739 * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, 740 * and finally to SEC_PKCS7DestroyContentInfo(). 741 * 742 * An error results in a return value of NULL and an error set. 743 * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 744 */ 745 746 SEC_PKCS7ContentInfo * 747 SECMIME_CreateSigned(CERTCertificate *scert, 748 CERTCertificate *ecert, 749 CERTCertDBHandle *certdb, 750 SECOidTag digestalg, 751 SECItem *digest, 752 SECKEYGetPasswordKey pwfn, 753 void *pwfn_arg) 754 { 755 SEC_PKCS7ContentInfo *cinfo; 756 SECStatus rv; 757 758 /* See note in header comment above about digestalg. */ 759 /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */ 760 761 cinfo = SEC_PKCS7CreateSignedData(scert, certUsageEmailSigner, 762 certdb, digestalg, digest, 763 pwfn, pwfn_arg); 764 if (cinfo == NULL) 765 return NULL; 766 767 if (SEC_PKCS7IncludeCertChain(cinfo, NULL) != SECSuccess) { 768 SEC_PKCS7DestroyContentInfo(cinfo); 769 return NULL; 770 } 771 772 /* if the encryption cert and the signing cert differ, then include 773 * the encryption cert too. 774 */ 775 /* it is ok to compare the pointers since we ref count, and the same 776 * cert will always have the same pointer 777 */ 778 if ((ecert != NULL) && (ecert != scert)) { 779 rv = SEC_PKCS7AddCertificate(cinfo, ecert); 780 if (rv != SECSuccess) { 781 SEC_PKCS7DestroyContentInfo(cinfo); 782 return NULL; 783 } 784 } 785 /* 786 * Add the signing time. But if it fails for some reason, 787 * may as well not give up altogether -- just assert. 788 */ 789 rv = SEC_PKCS7AddSigningTime(cinfo); 790 PORT_Assert(rv == SECSuccess); 791 792 /* 793 * Add the email profile. Again, if it fails for some reason, 794 * may as well not give up altogether -- just assert. 795 */ 796 rv = smime_add_profile(ecert, cinfo); 797 PORT_Assert(rv == SECSuccess); 798 799 return cinfo; 800 }