security/nss/lib/pkcs7/secmime.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial