1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pkcs7/secmime.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,822 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * Stuff specific to S/MIME policy and interoperability. 1.10 + * Depends on PKCS7, but there should be no dependency the other way around. 1.11 + */ 1.12 + 1.13 +#include "secmime.h" 1.14 +#include "secoid.h" 1.15 +#include "pk11func.h" 1.16 +#include "ciferfam.h" /* for CIPHER_FAMILY symbols */ 1.17 +#include "secasn1.h" 1.18 +#include "secitem.h" 1.19 +#include "cert.h" 1.20 +#include "key.h" 1.21 +#include "secerr.h" 1.22 + 1.23 +typedef struct smime_cipher_map_struct { 1.24 + unsigned long cipher; 1.25 + SECOidTag algtag; 1.26 + SECItem *parms; 1.27 +} smime_cipher_map; 1.28 + 1.29 +/* 1.30 + * These are macros because I think some subsequent parameters, 1.31 + * like those for RC5, will want to use them, too, separately. 1.32 + */ 1.33 +#define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10 1.34 +#define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28 1.35 +#define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40 1.36 +#define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 1.37 + 1.38 +#ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */ 1.39 +static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 }; 1.40 +#endif 1.41 +static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 }; 1.42 +static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 }; 1.43 +static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 }; 1.44 + 1.45 +static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) }; 1.46 +static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) }; 1.47 +static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) }; 1.48 + 1.49 +static smime_cipher_map smime_cipher_maps[] = { 1.50 + { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 }, 1.51 + { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 }, 1.52 + { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 }, 1.53 +#ifdef SMIME_DOES_RC5 1.54 + { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 }, 1.55 + { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 }, 1.56 + { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 }, 1.57 +#endif 1.58 + { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL }, 1.59 + { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL } 1.60 +}; 1.61 + 1.62 +/* 1.63 + * Note, the following value really just needs to be an upper bound 1.64 + * on the ciphers. 1.65 + */ 1.66 +static const int smime_symmetric_count = sizeof(smime_cipher_maps) 1.67 + / sizeof(smime_cipher_map); 1.68 + 1.69 +static unsigned long *smime_prefs, *smime_newprefs; 1.70 +static int smime_current_pref_index = 0; 1.71 +static PRBool smime_prefs_complete = PR_FALSE; 1.72 +static PRBool smime_prefs_changed = PR_TRUE; 1.73 + 1.74 +static unsigned long smime_policy_bits = 0; 1.75 + 1.76 + 1.77 +static int 1.78 +smime_mapi_by_cipher (unsigned long cipher) 1.79 +{ 1.80 + int i; 1.81 + 1.82 + for (i = 0; i < smime_symmetric_count; i++) { 1.83 + if (smime_cipher_maps[i].cipher == cipher) 1.84 + break; 1.85 + } 1.86 + 1.87 + if (i == smime_symmetric_count) 1.88 + return -1; 1.89 + 1.90 + return i; 1.91 +} 1.92 + 1.93 + 1.94 +/* 1.95 + * this function locally records the user's preference 1.96 + */ 1.97 +SECStatus 1.98 +SECMIME_EnableCipher(long which, int on) 1.99 +{ 1.100 + unsigned long mask; 1.101 + 1.102 + if (smime_newprefs == NULL || smime_prefs_complete) { 1.103 + /* 1.104 + * This is either the very first time, or we are starting over. 1.105 + */ 1.106 + smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count 1.107 + * sizeof(*smime_newprefs)); 1.108 + if (smime_newprefs == NULL) 1.109 + return SECFailure; 1.110 + smime_current_pref_index = 0; 1.111 + smime_prefs_complete = PR_FALSE; 1.112 + } 1.113 + 1.114 + mask = which & CIPHER_FAMILYID_MASK; 1.115 + if (mask == CIPHER_FAMILYID_MASK) { 1.116 + /* 1.117 + * This call signifies that all preferences have been set. 1.118 + * Move "newprefs" over, after checking first whether or 1.119 + * not the new ones are different from the old ones. 1.120 + */ 1.121 + if (smime_prefs != NULL) { 1.122 + if (PORT_Memcmp (smime_prefs, smime_newprefs, 1.123 + smime_symmetric_count * sizeof(*smime_prefs)) == 0) 1.124 + smime_prefs_changed = PR_FALSE; 1.125 + else 1.126 + smime_prefs_changed = PR_TRUE; 1.127 + PORT_Free (smime_prefs); 1.128 + } 1.129 + 1.130 + smime_prefs = smime_newprefs; 1.131 + smime_prefs_complete = PR_TRUE; 1.132 + return SECSuccess; 1.133 + } 1.134 + 1.135 + PORT_Assert (mask == CIPHER_FAMILYID_SMIME); 1.136 + if (mask != CIPHER_FAMILYID_SMIME) { 1.137 + /* XXX set an error! */ 1.138 + return SECFailure; 1.139 + } 1.140 + 1.141 + if (on) { 1.142 + PORT_Assert (smime_current_pref_index < smime_symmetric_count); 1.143 + if (smime_current_pref_index >= smime_symmetric_count) { 1.144 + /* XXX set an error! */ 1.145 + return SECFailure; 1.146 + } 1.147 + 1.148 + smime_newprefs[smime_current_pref_index++] = which; 1.149 + } 1.150 + 1.151 + return SECSuccess; 1.152 +} 1.153 + 1.154 + 1.155 +/* 1.156 + * this function locally records the export policy 1.157 + */ 1.158 +SECStatus 1.159 +SECMIME_SetPolicy(long which, int on) 1.160 +{ 1.161 + unsigned long mask; 1.162 + 1.163 + PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME); 1.164 + if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) { 1.165 + /* XXX set an error! */ 1.166 + return SECFailure; 1.167 + } 1.168 + 1.169 + which &= ~CIPHER_FAMILYID_MASK; 1.170 + 1.171 + PORT_Assert (which < 32); /* bits in the long */ 1.172 + if (which >= 32) { 1.173 + /* XXX set an error! */ 1.174 + return SECFailure; 1.175 + } 1.176 + 1.177 + mask = 1UL << which; 1.178 + 1.179 + if (on) { 1.180 + smime_policy_bits |= mask; 1.181 + } else { 1.182 + smime_policy_bits &= ~mask; 1.183 + } 1.184 + 1.185 + return SECSuccess; 1.186 +} 1.187 + 1.188 + 1.189 +/* 1.190 + * Based on the given algorithm (including its parameters, in some cases!) 1.191 + * and the given key (may or may not be inspected, depending on the 1.192 + * algorithm), find the appropriate policy algorithm specification 1.193 + * and return it. If no match can be made, -1 is returned. 1.194 + */ 1.195 +static long 1.196 +smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key) 1.197 +{ 1.198 + SECOidTag algtag; 1.199 + 1.200 + algtag = SECOID_GetAlgorithmTag (algid); 1.201 + switch (algtag) { 1.202 + case SEC_OID_RC2_CBC: 1.203 + { 1.204 + unsigned int keylen_bits; 1.205 + 1.206 + keylen_bits = PK11_GetKeyStrength (key, algid); 1.207 + switch (keylen_bits) { 1.208 + case 40: 1.209 + return SMIME_RC2_CBC_40; 1.210 + case 64: 1.211 + return SMIME_RC2_CBC_64; 1.212 + case 128: 1.213 + return SMIME_RC2_CBC_128; 1.214 + default: 1.215 + break; 1.216 + } 1.217 + } 1.218 + break; 1.219 + case SEC_OID_DES_CBC: 1.220 + return SMIME_DES_CBC_56; 1.221 + case SEC_OID_DES_EDE3_CBC: 1.222 + return SMIME_DES_EDE3_168; 1.223 +#ifdef SMIME_DOES_RC5 1.224 + case SEC_OID_RC5_CBC_PAD: 1.225 + PORT_Assert (0); /* XXX need to pull out parameters and match */ 1.226 + break; 1.227 +#endif 1.228 + default: 1.229 + break; 1.230 + } 1.231 + 1.232 + return -1; 1.233 +} 1.234 + 1.235 + 1.236 +static PRBool 1.237 +smime_cipher_allowed (unsigned long which) 1.238 +{ 1.239 + unsigned long mask; 1.240 + 1.241 + which &= ~CIPHER_FAMILYID_MASK; 1.242 + PORT_Assert (which < 32); /* bits per long (min) */ 1.243 + if (which >= 32) 1.244 + return PR_FALSE; 1.245 + 1.246 + mask = 1UL << which; 1.247 + if ((mask & smime_policy_bits) == 0) 1.248 + return PR_FALSE; 1.249 + 1.250 + return PR_TRUE; 1.251 +} 1.252 + 1.253 + 1.254 +PRBool 1.255 +SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key) 1.256 +{ 1.257 + long which; 1.258 + 1.259 + which = smime_policy_algorithm (algid, key); 1.260 + if (which < 0) 1.261 + return PR_FALSE; 1.262 + 1.263 + return smime_cipher_allowed ((unsigned long)which); 1.264 +} 1.265 + 1.266 + 1.267 +/* 1.268 + * Does the current policy allow *any* S/MIME encryption (or decryption)? 1.269 + * 1.270 + * This tells whether or not *any* S/MIME encryption can be done, 1.271 + * according to policy. Callers may use this to do nicer user interface 1.272 + * (say, greying out a checkbox so a user does not even try to encrypt 1.273 + * a message when they are not allowed to) or for any reason they want 1.274 + * to check whether S/MIME encryption (or decryption, for that matter) 1.275 + * may be done. 1.276 + * 1.277 + * It takes no arguments. The return value is a simple boolean: 1.278 + * PR_TRUE means encryption (or decryption) is *possible* 1.279 + * (but may still fail due to other reasons, like because we cannot 1.280 + * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) 1.281 + * PR_FALSE means encryption (or decryption) is not permitted 1.282 + * 1.283 + * There are no errors from this routine. 1.284 + */ 1.285 +PRBool 1.286 +SECMIME_EncryptionPossible (void) 1.287 +{ 1.288 + if (smime_policy_bits != 0) 1.289 + return PR_TRUE; 1.290 + 1.291 + return PR_FALSE; 1.292 +} 1.293 + 1.294 + 1.295 +/* 1.296 + * XXX Would like the "parameters" field to be a SECItem *, but the 1.297 + * encoder is having trouble with optional pointers to an ANY. Maybe 1.298 + * once that is fixed, can change this back... 1.299 + */ 1.300 +typedef struct smime_capability_struct { 1.301 + unsigned long cipher; /* local; not part of encoding */ 1.302 + SECOidTag capIDTag; /* local; not part of encoding */ 1.303 + SECItem capabilityID; 1.304 + SECItem parameters; 1.305 +} smime_capability; 1.306 + 1.307 +static const SEC_ASN1Template smime_capability_template[] = { 1.308 + { SEC_ASN1_SEQUENCE, 1.309 + 0, NULL, sizeof(smime_capability) }, 1.310 + { SEC_ASN1_OBJECT_ID, 1.311 + offsetof(smime_capability,capabilityID), }, 1.312 + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, 1.313 + offsetof(smime_capability,parameters), }, 1.314 + { 0, } 1.315 +}; 1.316 + 1.317 +static const SEC_ASN1Template smime_capabilities_template[] = { 1.318 + { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template } 1.319 +}; 1.320 + 1.321 + 1.322 + 1.323 +static void 1.324 +smime_fill_capability (smime_capability *cap) 1.325 +{ 1.326 + unsigned long cipher; 1.327 + SECOidTag algtag; 1.328 + int i; 1.329 + 1.330 + algtag = SECOID_FindOIDTag (&(cap->capabilityID)); 1.331 + 1.332 + for (i = 0; i < smime_symmetric_count; i++) { 1.333 + if (smime_cipher_maps[i].algtag != algtag) 1.334 + continue; 1.335 + /* 1.336 + * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing 1.337 + * 2 NULLs as equal and NULL and non-NULL as not equal), we could 1.338 + * use that here instead of all of the following comparison code. 1.339 + */ 1.340 + if (cap->parameters.data != NULL) { 1.341 + if (smime_cipher_maps[i].parms == NULL) 1.342 + continue; 1.343 + if (cap->parameters.len != smime_cipher_maps[i].parms->len) 1.344 + continue; 1.345 + if (PORT_Memcmp (cap->parameters.data, 1.346 + smime_cipher_maps[i].parms->data, 1.347 + cap->parameters.len) == 0) 1.348 + break; 1.349 + } else if (smime_cipher_maps[i].parms == NULL) { 1.350 + break; 1.351 + } 1.352 + } 1.353 + 1.354 + if (i == smime_symmetric_count) 1.355 + cipher = 0; 1.356 + else 1.357 + cipher = smime_cipher_maps[i].cipher; 1.358 + 1.359 + cap->cipher = cipher; 1.360 + cap->capIDTag = algtag; 1.361 +} 1.362 + 1.363 + 1.364 +static long 1.365 +smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts) 1.366 +{ 1.367 + PLArenaPool *poolp; 1.368 + long chosen_cipher; 1.369 + int *cipher_abilities; 1.370 + int *cipher_votes; 1.371 + int strong_mapi; 1.372 + int rcount, mapi, max; 1.373 + 1.374 + if (smime_policy_bits == 0) { 1.375 + PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM); 1.376 + return -1; 1.377 + } 1.378 + 1.379 + chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ 1.380 + 1.381 + poolp = PORT_NewArena (1024); /* XXX what is right value? */ 1.382 + if (poolp == NULL) 1.383 + goto done; 1.384 + 1.385 + cipher_abilities = (int*)PORT_ArenaZAlloc (poolp, 1.386 + smime_symmetric_count * sizeof(int)); 1.387 + if (cipher_abilities == NULL) 1.388 + goto done; 1.389 + 1.390 + cipher_votes = (int*)PORT_ArenaZAlloc (poolp, 1.391 + smime_symmetric_count * sizeof(int)); 1.392 + if (cipher_votes == NULL) 1.393 + goto done; 1.394 + 1.395 + /* 1.396 + * XXX Should have a #define somewhere which specifies default 1.397 + * strong cipher. (Or better, a way to configure.) 1.398 + */ 1.399 + 1.400 + /* Make triple-DES the strong cipher. */ 1.401 + strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168); 1.402 + 1.403 + PORT_Assert (strong_mapi >= 0); 1.404 + 1.405 + for (rcount = 0; rcerts[rcount] != NULL; rcount++) { 1.406 + SECItem *profile; 1.407 + smime_capability **caps; 1.408 + int capi, pref; 1.409 + SECStatus dstat; 1.410 + 1.411 + pref = smime_symmetric_count; 1.412 + profile = CERT_FindSMimeProfile (rcerts[rcount]); 1.413 + if (profile != NULL && profile->data != NULL && profile->len > 0) { 1.414 + caps = NULL; 1.415 + dstat = SEC_QuickDERDecodeItem (poolp, &caps, 1.416 + smime_capabilities_template, 1.417 + profile); 1.418 + if (dstat == SECSuccess && caps != NULL) { 1.419 + for (capi = 0; caps[capi] != NULL; capi++) { 1.420 + smime_fill_capability (caps[capi]); 1.421 + mapi = smime_mapi_by_cipher (caps[capi]->cipher); 1.422 + if (mapi >= 0) { 1.423 + cipher_abilities[mapi]++; 1.424 + cipher_votes[mapi] += pref; 1.425 + --pref; 1.426 + } 1.427 + } 1.428 + } 1.429 + } else { 1.430 + SECKEYPublicKey *key; 1.431 + unsigned int pklen_bits; 1.432 + 1.433 + /* 1.434 + * XXX This is probably only good for RSA keys. What I would 1.435 + * really like is a function to just say; Is the public key in 1.436 + * this cert an export-length key? Then I would not have to 1.437 + * know things like the value 512, or the kind of key, or what 1.438 + * a subjectPublicKeyInfo is, etc. 1.439 + */ 1.440 + key = CERT_ExtractPublicKey (rcerts[rcount]); 1.441 + if (key != NULL) { 1.442 + pklen_bits = SECKEY_PublicKeyStrength (key) * 8; 1.443 + SECKEY_DestroyPublicKey (key); 1.444 + 1.445 + if (pklen_bits > 512) { 1.446 + cipher_abilities[strong_mapi]++; 1.447 + cipher_votes[strong_mapi] += pref; 1.448 + } 1.449 + } 1.450 + } 1.451 + if (profile != NULL) 1.452 + SECITEM_FreeItem (profile, PR_TRUE); 1.453 + } 1.454 + 1.455 + max = 0; 1.456 + for (mapi = 0; mapi < smime_symmetric_count; mapi++) { 1.457 + if (cipher_abilities[mapi] != rcount) 1.458 + continue; 1.459 + if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher)) 1.460 + continue; 1.461 + if (cipher_votes[mapi] > max) { 1.462 + chosen_cipher = smime_cipher_maps[mapi].cipher; 1.463 + max = cipher_votes[mapi]; 1.464 + } /* XXX else if a tie, let scert break it? */ 1.465 + } 1.466 + 1.467 +done: 1.468 + if (poolp != NULL) 1.469 + PORT_FreeArena (poolp, PR_FALSE); 1.470 + 1.471 + return chosen_cipher; 1.472 +} 1.473 + 1.474 + 1.475 +/* 1.476 + * XXX This is a hack for now to satisfy our current interface. 1.477 + * Eventually, with more parameters needing to be specified, just 1.478 + * looking up the keysize is not going to be sufficient. 1.479 + */ 1.480 +static int 1.481 +smime_keysize_by_cipher (unsigned long which) 1.482 +{ 1.483 + int keysize; 1.484 + 1.485 + switch (which) { 1.486 + case SMIME_RC2_CBC_40: 1.487 + keysize = 40; 1.488 + break; 1.489 + case SMIME_RC2_CBC_64: 1.490 + keysize = 64; 1.491 + break; 1.492 + case SMIME_RC2_CBC_128: 1.493 + keysize = 128; 1.494 + break; 1.495 +#ifdef SMIME_DOES_RC5 1.496 + case SMIME_RC5PAD_64_16_40: 1.497 + case SMIME_RC5PAD_64_16_64: 1.498 + case SMIME_RC5PAD_64_16_128: 1.499 + /* XXX See comment above; keysize is not enough... */ 1.500 + PORT_Assert (0); 1.501 + PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); 1.502 + keysize = -1; 1.503 + break; 1.504 +#endif 1.505 + case SMIME_DES_CBC_56: 1.506 + case SMIME_DES_EDE3_168: 1.507 + /* 1.508 + * These are special; since the key size is fixed, we actually 1.509 + * want to *avoid* specifying a key size. 1.510 + */ 1.511 + keysize = 0; 1.512 + break; 1.513 + default: 1.514 + keysize = -1; 1.515 + break; 1.516 + } 1.517 + 1.518 + return keysize; 1.519 +} 1.520 + 1.521 + 1.522 +/* 1.523 + * Start an S/MIME encrypting context. 1.524 + * 1.525 + * "scert" is the cert for the sender. It will be checked for validity. 1.526 + * "rcerts" are the certs for the recipients. They will also be checked. 1.527 + * 1.528 + * "certdb" is the cert database to use for verifying the certs. 1.529 + * It can be NULL if a default database is available (like in the client). 1.530 + * 1.531 + * This function already does all of the stuff specific to S/MIME protocol 1.532 + * and local policy; the return value just needs to be passed to 1.533 + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, 1.534 + * and finally to SEC_PKCS7DestroyContentInfo(). 1.535 + * 1.536 + * An error results in a return value of NULL and an error set. 1.537 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.538 + */ 1.539 +SEC_PKCS7ContentInfo * 1.540 +SECMIME_CreateEncrypted(CERTCertificate *scert, 1.541 + CERTCertificate **rcerts, 1.542 + CERTCertDBHandle *certdb, 1.543 + SECKEYGetPasswordKey pwfn, 1.544 + void *pwfn_arg) 1.545 +{ 1.546 + SEC_PKCS7ContentInfo *cinfo; 1.547 + long cipher; 1.548 + SECOidTag encalg; 1.549 + int keysize; 1.550 + int mapi, rci; 1.551 + 1.552 + cipher = smime_choose_cipher (scert, rcerts); 1.553 + if (cipher < 0) 1.554 + return NULL; 1.555 + 1.556 + mapi = smime_mapi_by_cipher (cipher); 1.557 + if (mapi < 0) 1.558 + return NULL; 1.559 + 1.560 + /* 1.561 + * XXX This is stretching it -- CreateEnvelopedData should probably 1.562 + * take a cipher itself of some sort, because we cannot know what the 1.563 + * future will bring in terms of parameters for each type of algorithm. 1.564 + * For example, just an algorithm and keysize is *not* sufficient to 1.565 + * fully specify the usage of RC5 (which also needs to know rounds and 1.566 + * block size). Work this out into a better API! 1.567 + */ 1.568 + encalg = smime_cipher_maps[mapi].algtag; 1.569 + keysize = smime_keysize_by_cipher (cipher); 1.570 + if (keysize < 0) 1.571 + return NULL; 1.572 + 1.573 + cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient, 1.574 + certdb, encalg, keysize, 1.575 + pwfn, pwfn_arg); 1.576 + if (cinfo == NULL) 1.577 + return NULL; 1.578 + 1.579 + for (rci = 0; rcerts[rci] != NULL; rci++) { 1.580 + if (rcerts[rci] == scert) 1.581 + continue; 1.582 + if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient, 1.583 + NULL) != SECSuccess) { 1.584 + SEC_PKCS7DestroyContentInfo (cinfo); 1.585 + return NULL; 1.586 + } 1.587 + } 1.588 + 1.589 + return cinfo; 1.590 +} 1.591 + 1.592 + 1.593 +static smime_capability **smime_capabilities; 1.594 +static SECItem *smime_encoded_caps; 1.595 + 1.596 + 1.597 +static SECStatus 1.598 +smime_init_caps (void) 1.599 +{ 1.600 + smime_capability *cap; 1.601 + smime_cipher_map *map; 1.602 + SECOidData *oiddata; 1.603 + SECStatus rv; 1.604 + int i; 1.605 + 1.606 + if (smime_encoded_caps != NULL && (! smime_prefs_changed)) 1.607 + return SECSuccess; 1.608 + 1.609 + if (smime_encoded_caps != NULL) { 1.610 + SECITEM_FreeItem (smime_encoded_caps, PR_TRUE); 1.611 + smime_encoded_caps = NULL; 1.612 + } 1.613 + 1.614 + if (smime_capabilities == NULL) { 1.615 + smime_capabilities = (smime_capability**)PORT_ZAlloc ( 1.616 + (smime_symmetric_count + 1) 1.617 + * sizeof(smime_capability *)); 1.618 + if (smime_capabilities == NULL) 1.619 + return SECFailure; 1.620 + } 1.621 + 1.622 + rv = SECFailure; 1.623 + 1.624 + /* 1.625 + The process of creating the encoded PKCS7 cipher capability list 1.626 + involves two basic steps: 1.627 + 1.628 + (a) Convert our internal representation of cipher preferences 1.629 + (smime_prefs) into an array containing cipher OIDs and 1.630 + parameter data (smime_capabilities). This step is 1.631 + performed here. 1.632 + 1.633 + (b) Encode, using ASN.1, the cipher information in 1.634 + smime_capabilities, leaving the encoded result in 1.635 + smime_encoded_caps. 1.636 + 1.637 + (In the process of performing (a), Lisa put in some optimizations 1.638 + which allow us to avoid needlessly re-populating elements in 1.639 + smime_capabilities as we walk through smime_prefs.) 1.640 + */ 1.641 + for (i = 0; i < smime_current_pref_index; i++) { 1.642 + int mapi; 1.643 + 1.644 + /* Get the next cipher preference in smime_prefs. */ 1.645 + mapi = smime_mapi_by_cipher (smime_prefs[i]); 1.646 + if (mapi < 0) 1.647 + break; 1.648 + 1.649 + /* Find the corresponding entry in the cipher map. */ 1.650 + PORT_Assert (mapi < smime_symmetric_count); 1.651 + map = &(smime_cipher_maps[mapi]); 1.652 + 1.653 + /* 1.654 + * Convert the next preference found in smime_prefs into an 1.655 + * smime_capability. 1.656 + */ 1.657 + 1.658 + cap = smime_capabilities[i]; 1.659 + if (cap == NULL) { 1.660 + cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability)); 1.661 + if (cap == NULL) 1.662 + break; 1.663 + smime_capabilities[i] = cap; 1.664 + } else if (cap->cipher == smime_prefs[i]) { 1.665 + continue; /* no change to this one */ 1.666 + } 1.667 + 1.668 + cap->capIDTag = map->algtag; 1.669 + oiddata = SECOID_FindOIDByTag (map->algtag); 1.670 + if (oiddata == NULL) 1.671 + break; 1.672 + 1.673 + if (cap->capabilityID.data != NULL) { 1.674 + SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); 1.675 + cap->capabilityID.data = NULL; 1.676 + cap->capabilityID.len = 0; 1.677 + } 1.678 + 1.679 + rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid)); 1.680 + if (rv != SECSuccess) 1.681 + break; 1.682 + 1.683 + if (map->parms == NULL) { 1.684 + cap->parameters.data = NULL; 1.685 + cap->parameters.len = 0; 1.686 + } else { 1.687 + cap->parameters.data = map->parms->data; 1.688 + cap->parameters.len = map->parms->len; 1.689 + } 1.690 + 1.691 + cap->cipher = smime_prefs[i]; 1.692 + } 1.693 + 1.694 + if (i != smime_current_pref_index) 1.695 + return rv; 1.696 + 1.697 + while (i < smime_symmetric_count) { 1.698 + cap = smime_capabilities[i]; 1.699 + if (cap != NULL) { 1.700 + SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); 1.701 + PORT_Free (cap); 1.702 + } 1.703 + smime_capabilities[i] = NULL; 1.704 + i++; 1.705 + } 1.706 + smime_capabilities[i] = NULL; 1.707 + 1.708 + smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities, 1.709 + smime_capabilities_template); 1.710 + if (smime_encoded_caps == NULL) 1.711 + return SECFailure; 1.712 + 1.713 + return SECSuccess; 1.714 +} 1.715 + 1.716 + 1.717 +static SECStatus 1.718 +smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo) 1.719 +{ 1.720 + PORT_Assert (smime_prefs_complete); 1.721 + if (! smime_prefs_complete) 1.722 + return SECFailure; 1.723 + 1.724 + /* For that matter, if capabilities haven't been initialized yet, 1.725 + do so now. */ 1.726 + if (smime_encoded_caps == NULL || smime_prefs_changed) { 1.727 + SECStatus rv; 1.728 + 1.729 + rv = smime_init_caps(); 1.730 + if (rv != SECSuccess) 1.731 + return rv; 1.732 + 1.733 + PORT_Assert (smime_encoded_caps != NULL); 1.734 + } 1.735 + 1.736 + return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES, 1.737 + smime_encoded_caps); 1.738 +} 1.739 + 1.740 + 1.741 +/* 1.742 + * Start an S/MIME signing context. 1.743 + * 1.744 + * "scert" is the cert that will be used to sign the data. It will be 1.745 + * checked for validity. 1.746 + * 1.747 + * "ecert" is the signer's encryption cert. If it is different from 1.748 + * scert, then it will be included in the signed message so that the 1.749 + * recipient can save it for future encryptions. 1.750 + * 1.751 + * "certdb" is the cert database to use for verifying the cert. 1.752 + * It can be NULL if a default database is available (like in the client). 1.753 + * 1.754 + * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). 1.755 + * XXX There should be SECMIME functions for hashing, or the hashing should 1.756 + * be built into this interface, which we would like because we would 1.757 + * support more smartcards that way, and then this argument should go away.) 1.758 + * 1.759 + * "digest" is the actual digest of the data. It must be provided in 1.760 + * the case of detached data or NULL if the content will be included. 1.761 + * 1.762 + * This function already does all of the stuff specific to S/MIME protocol 1.763 + * and local policy; the return value just needs to be passed to 1.764 + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, 1.765 + * and finally to SEC_PKCS7DestroyContentInfo(). 1.766 + * 1.767 + * An error results in a return value of NULL and an error set. 1.768 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.769 + */ 1.770 + 1.771 +SEC_PKCS7ContentInfo * 1.772 +SECMIME_CreateSigned (CERTCertificate *scert, 1.773 + CERTCertificate *ecert, 1.774 + CERTCertDBHandle *certdb, 1.775 + SECOidTag digestalg, 1.776 + SECItem *digest, 1.777 + SECKEYGetPasswordKey pwfn, 1.778 + void *pwfn_arg) 1.779 +{ 1.780 + SEC_PKCS7ContentInfo *cinfo; 1.781 + SECStatus rv; 1.782 + 1.783 + /* See note in header comment above about digestalg. */ 1.784 + /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */ 1.785 + 1.786 + cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner, 1.787 + certdb, digestalg, digest, 1.788 + pwfn, pwfn_arg); 1.789 + if (cinfo == NULL) 1.790 + return NULL; 1.791 + 1.792 + if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) { 1.793 + SEC_PKCS7DestroyContentInfo (cinfo); 1.794 + return NULL; 1.795 + } 1.796 + 1.797 + /* if the encryption cert and the signing cert differ, then include 1.798 + * the encryption cert too. 1.799 + */ 1.800 + /* it is ok to compare the pointers since we ref count, and the same 1.801 + * cert will always have the same pointer 1.802 + */ 1.803 + if ( ( ecert != NULL ) && ( ecert != scert ) ) { 1.804 + rv = SEC_PKCS7AddCertificate(cinfo, ecert); 1.805 + if ( rv != SECSuccess ) { 1.806 + SEC_PKCS7DestroyContentInfo (cinfo); 1.807 + return NULL; 1.808 + } 1.809 + } 1.810 + /* 1.811 + * Add the signing time. But if it fails for some reason, 1.812 + * may as well not give up altogether -- just assert. 1.813 + */ 1.814 + rv = SEC_PKCS7AddSigningTime (cinfo); 1.815 + PORT_Assert (rv == SECSuccess); 1.816 + 1.817 + /* 1.818 + * Add the email profile. Again, if it fails for some reason, 1.819 + * may as well not give up altogether -- just assert. 1.820 + */ 1.821 + rv = smime_add_profile (ecert, cinfo); 1.822 + PORT_Assert (rv == SECSuccess); 1.823 + 1.824 + return cinfo; 1.825 +}