smimeutil.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:22k
源码类别:

CA认证

开发平台:

WINDOWS

  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.  * Stuff specific to S/MIME policy and interoperability.
  35.  *
  36.  * $Id: smimeutil.c,v 1.4 2000/06/20 16:28:57 chrisk%netscape.com Exp $
  37.  */
  38. #include "secmime.h"
  39. #include "secoid.h"
  40. #include "pk11func.h"
  41. #include "ciferfam.h" /* for CIPHER_FAMILY symbols */
  42. #include "secasn1.h"
  43. #include "secitem.h"
  44. #include "cert.h"
  45. #include "key.h"
  46. #include "secerr.h"
  47. #include "cms.h"
  48. /* various integer's ASN.1 encoding */
  49. static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
  50. static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
  51. static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
  52. /* RC2 algorithm parameters (used in smime_cipher_map) */
  53. static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
  54. static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
  55. static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
  56. /*
  57.  * XXX Would like the "parameters" field to be a SECItem *, but the
  58.  * encoder is having trouble with optional pointers to an ANY.  Maybe
  59.  * once that is fixed, can change this back...
  60.  */
  61. typedef struct {
  62.     SECItem capabilityID;
  63.     SECItem parameters;
  64.     long cipher; /* optimization */
  65. } NSSSMIMECapability;
  66. static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
  67.     { SEC_ASN1_SEQUENCE,
  68.   0, NULL, sizeof(NSSSMIMECapability) },
  69.     { SEC_ASN1_OBJECT_ID,
  70.   offsetof(NSSSMIMECapability,capabilityID), },
  71.     { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
  72.   offsetof(NSSSMIMECapability,parameters), },
  73.     { 0, }
  74. };
  75. static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
  76.     { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
  77. };
  78. /*
  79.  * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
  80.  *  to store this and only this certificate permanently for the sender email address.
  81.  */
  82. typedef enum {
  83.     NSSSMIMEEncryptionKeyPref_IssuerSN,
  84.     NSSSMIMEEncryptionKeyPref_RKeyID,
  85.     NSSSMIMEEncryptionKeyPref_SubjectKeyID
  86. } NSSSMIMEEncryptionKeyPrefSelector;
  87. typedef struct {
  88.     NSSSMIMEEncryptionKeyPrefSelector selector;
  89.     union {
  90. CERTIssuerAndSN *issuerAndSN;
  91. NSSCMSRecipientKeyIdentifier *recipientKeyID;
  92. SECItem *subjectKeyID;
  93.     } id;
  94. } NSSSMIMEEncryptionKeyPreference;
  95. extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
  96. static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
  97.     { SEC_ASN1_CHOICE,
  98.   offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
  99.   sizeof(NSSSMIMEEncryptionKeyPreference) },
  100.     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 0,
  101.   offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
  102.   CERT_IssuerAndSNTemplate,
  103.   NSSSMIMEEncryptionKeyPref_IssuerSN },
  104.     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1,
  105.   offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
  106.   NSSCMSRecipientKeyIdentifierTemplate,
  107.   NSSSMIMEEncryptionKeyPref_IssuerSN },
  108.     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 2,
  109.   offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
  110.   SEC_OctetStringTemplate,
  111.   NSSSMIMEEncryptionKeyPref_SubjectKeyID },
  112.     { 0, }
  113. };
  114. /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
  115. typedef struct {
  116.     unsigned long cipher;
  117.     SECOidTag algtag;
  118.     SECItem *parms;
  119.     PRBool enabled; /* in the user's preferences */
  120.     PRBool allowed; /* per export policy */
  121. } smime_cipher_map_entry;
  122. /* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
  123. static smime_cipher_map_entry smime_cipher_map[] = {
  124. /*    cipher algtag parms enabled  allowed */
  125. /*    ---------------------------------------------------------------------------------- */
  126.     { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_int40, PR_TRUE, PR_TRUE },
  127.     { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
  128.     { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_TRUE, PR_TRUE },
  129.     { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
  130.     { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
  131.     { SMIME_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL, PR_TRUE, PR_TRUE }
  132. };
  133. static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
  134. /*
  135.  * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
  136.  */
  137. static int
  138. smime_mapi_by_cipher(unsigned long cipher)
  139. {
  140.     int i;
  141.     for (i = 0; i < smime_cipher_map_count; i++) {
  142. if (smime_cipher_map[i].cipher == cipher)
  143.     return i; /* bingo */
  144.     }
  145.     return -1; /* should not happen if we're consistent, right? */
  146. }
  147. /*
  148.  * NSS_SMIME_EnableCipher - this function locally records the user's preference
  149.  */
  150. SECStatus 
  151. NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
  152. {
  153.     unsigned long mask;
  154.     int mapi;
  155.     mask = which & CIPHER_FAMILYID_MASK;
  156.     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
  157.     if (mask != CIPHER_FAMILYID_SMIME)
  158. /* XXX set an error! */
  159.      return SECFailure;
  160.     mapi = smime_mapi_by_cipher(which);
  161.     if (mapi < 0)
  162. /* XXX set an error */
  163. return SECFailure;
  164.     /* do we try to turn on a forbidden cipher? */
  165.     if (!smime_cipher_map[mapi].allowed && on) {
  166. PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
  167. return SECFailure;
  168.     }
  169.     if (smime_cipher_map[mapi].enabled != on)
  170. smime_cipher_map[mapi].enabled = on;
  171.     return SECSuccess;
  172. }
  173. /*
  174.  * this function locally records the export policy
  175.  */
  176. SECStatus 
  177. NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
  178. {
  179.     unsigned long mask;
  180.     int mapi;
  181.     mask = which & CIPHER_FAMILYID_MASK;
  182.     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
  183.     if (mask != CIPHER_FAMILYID_SMIME)
  184. /* XXX set an error! */
  185.      return SECFailure;
  186.     mapi = smime_mapi_by_cipher(which);
  187.     if (mapi < 0)
  188. /* XXX set an error */
  189. return SECFailure;
  190.     if (smime_cipher_map[mapi].allowed != on)
  191. smime_cipher_map[mapi].allowed = on;
  192.     return SECSuccess;
  193. }
  194. /*
  195.  * Based on the given algorithm (including its parameters, in some cases!)
  196.  * and the given key (may or may not be inspected, depending on the
  197.  * algorithm), find the appropriate policy algorithm specification
  198.  * and return it.  If no match can be made, -1 is returned.
  199.  */
  200. static SECStatus
  201. nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
  202. {
  203.     SECOidTag algtag;
  204.     unsigned int keylen_bits;
  205.     SECStatus rv = SECSuccess;
  206.     unsigned long c;
  207.     algtag = SECOID_GetAlgorithmTag(algid);
  208.     switch (algtag) {
  209.     case SEC_OID_RC2_CBC:
  210. keylen_bits = PK11_GetKeyStrength(key, algid);
  211. switch (keylen_bits) {
  212. case 40:
  213.     c = SMIME_RC2_CBC_40;
  214. case 64:
  215.     c = SMIME_RC2_CBC_64;
  216. case 128:
  217.     c = SMIME_RC2_CBC_128;
  218. default:
  219.     rv = SECFailure;
  220.     break;
  221. }
  222. break;
  223.     case SEC_OID_DES_CBC:
  224. c = SMIME_DES_CBC_56;
  225.     case SEC_OID_DES_EDE3_CBC:
  226. c = SMIME_DES_EDE3_168;
  227.     case SEC_OID_FORTEZZA_SKIPJACK:
  228. c = SMIME_FORTEZZA;
  229.     default:
  230. rv = SECFailure;
  231.     }
  232.     if (rv == SECSuccess)
  233. *cipher = c;
  234.     return rv;
  235. }
  236. static PRBool
  237. nss_smime_cipher_allowed(unsigned long which)
  238. {
  239.     int mapi;
  240.     mapi = smime_mapi_by_cipher(which);
  241.     if (mapi < 0)
  242. return PR_FALSE;
  243.     return smime_cipher_map[mapi].allowed;
  244. }
  245. PRBool
  246. NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
  247. {
  248.     unsigned long which;
  249.     if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
  250. return PR_FALSE;
  251.     return nss_smime_cipher_allowed(which);
  252. }
  253. /*
  254.  * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
  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. NSS_SMIMEUtil_EncryptionPossible(void)
  273. {
  274.     int i;
  275.     for (i = 0; i < smime_cipher_map_count; i++) {
  276. if (smime_cipher_map[i].allowed)
  277.     return PR_TRUE;
  278.     }
  279.     return PR_FALSE;
  280. }
  281. static int
  282. nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
  283. {
  284.     int i;
  285.     SECOidTag capIDTag;
  286.     /* we need the OIDTag here */
  287.     capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
  288.     /* go over all the SMIME ciphers we know and see if we find a match */
  289.     for (i = 0; i < smime_cipher_map_count; i++) {
  290. if (smime_cipher_map[i].algtag != capIDTag)
  291.     continue;
  292. /*
  293.  * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
  294.  * 2 NULLs as equal and NULL and non-NULL as not equal), we could
  295.  * use that here instead of all of the following comparison code.
  296.  */
  297. if (cap->parameters.data == NULL && smime_cipher_map[i].parms == NULL)
  298.     break; /* both empty: bingo */
  299. if (cap->parameters.data != NULL && smime_cipher_map[i].parms != NULL &&
  300.     cap->parameters.len == smime_cipher_map[i].parms->len &&
  301.     PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
  302.      cap->parameters.len) == 0)
  303. {
  304.     break; /* both not empty, same length & equal content: bingo */
  305. }
  306.     }
  307.     if (i == smime_cipher_map_count)
  308. return 0; /* no match found */
  309.     else
  310. return smime_cipher_map[i].cipher; /* match found, point to cipher */
  311. }
  312. /*
  313.  * smime_choose_cipher - choose a cipher that works for all the recipients
  314.  *
  315.  * "scert"  - sender's certificate
  316.  * "rcerts" - recipient's certificates
  317.  */
  318. static long
  319. smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
  320. {
  321.     PRArenaPool *poolp;
  322.     long cipher;
  323.     long chosen_cipher;
  324.     int *cipher_abilities;
  325.     int *cipher_votes;
  326.     int weak_mapi;
  327.     int strong_mapi;
  328.     int rcount, mapi, max, i;
  329.     PRBool scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
  330.     chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
  331.     weak_mapi = smime_mapi_by_cipher(chosen_cipher);
  332.     poolp = PORT_NewArena (1024); /* XXX what is right value? */
  333.     if (poolp == NULL)
  334. goto done;
  335.     cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
  336.     cipher_votes     = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
  337.     if (cipher_votes == NULL || cipher_abilities == NULL)
  338. goto done;
  339.     /* If the user has the Fortezza preference turned on, make
  340.      *  that the strong cipher. Otherwise, use triple-DES. */
  341.     strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
  342.     if (scert_is_fortezza) {
  343. mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
  344. if (mapi >= 0 && smime_cipher_map[mapi].enabled)
  345.     strong_mapi = mapi;
  346.     }
  347.     /* walk all the recipient's certs */
  348.     for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
  349. SECItem *profile;
  350. NSSSMIMECapability **caps;
  351. int pref;
  352. /* the first cipher that matches in the user's SMIME profile gets
  353.  * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
  354.  * and so on. If every cipher matches, the last one gets 1 (one) vote */
  355. pref = smime_cipher_map_count;
  356. /* find recipient's SMIME profile */
  357. profile = CERT_FindSMimeProfile(rcerts[rcount]);
  358. if (profile != NULL && profile->data != NULL && profile->len > 0) {
  359.     /* we have a profile (still DER-encoded) */
  360.     caps = NULL;
  361.     /* decode it */
  362.     if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
  363.     caps != NULL)
  364.     {
  365. /* walk the SMIME capabilities for this recipient */
  366. for (i = 0; caps[i] != NULL; i++) {
  367.     cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
  368.     mapi = smime_mapi_by_cipher(cipher);
  369.     if (mapi >= 0) {
  370. /* found the cipher */
  371. cipher_abilities[mapi]++;
  372. cipher_votes[mapi] += pref;
  373. --pref;
  374.     }
  375. }
  376.     }
  377. } else {
  378.     /* no profile found - so we can only assume that the user can do
  379.      * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
  380.     SECKEYPublicKey *key;
  381.     unsigned int pklen_bits;
  382.     /*
  383.      * if recipient's public key length is > 512, vote for a strong cipher
  384.      * please not that the side effect of this is that if only one recipient
  385.      * has an export-level public key, the strong cipher is disabled.
  386.      *
  387.      * XXX This is probably only good for RSA keys.  What I would
  388.      * really like is a function to just say;  Is the public key in
  389.      * this cert an export-length key?  Then I would not have to
  390.      * know things like the value 512, or the kind of key, or what
  391.      * a subjectPublicKeyInfo is, etc.
  392.      */
  393.     key = CERT_ExtractPublicKey(rcerts[rcount]);
  394.     pklen_bits = 0;
  395.     if (key != NULL) {
  396. pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
  397. SECKEY_DestroyPublicKey (key);
  398.     }
  399.     if (pklen_bits > 512) {
  400. /* cast votes for the strong algorithm */
  401. cipher_abilities[strong_mapi]++;
  402. cipher_votes[strong_mapi] += pref;
  403. pref--;
  404.     } 
  405.     /* always cast (possibly less) votes for the weak algorithm */
  406.     cipher_abilities[weak_mapi]++;
  407.     cipher_votes[weak_mapi] += pref;
  408. }
  409. if (profile != NULL)
  410.     SECITEM_FreeItem(profile, PR_TRUE);
  411.     }
  412.     /* find cipher that is agreeable by all recipients and that has the most votes */
  413.     max = 0;
  414.     for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
  415. /* if not all of the recipients can do this, forget it */
  416. if (cipher_abilities[mapi] != rcount)
  417.     continue;
  418. /* if cipher is not enabled or not allowed by policy, forget it */
  419. if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
  420.     continue;
  421. /* if we're not doing fortezza, but the cipher is fortezza, forget it */
  422. if (!scert_is_fortezza  && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
  423.     continue;
  424. /* now see if this one has more votes than the last best one */
  425. if (cipher_votes[mapi] >= max) {
  426.     /* if equal number of votes, prefer the ones further down in the list */
  427.     /* with the expectation that these are higher rated ciphers */
  428.     chosen_cipher = smime_cipher_map[mapi].cipher;
  429.     max = cipher_votes[mapi];
  430. }
  431.     }
  432.     /* if no common cipher was found, chosen_cipher stays at the default */
  433. done:
  434.     if (poolp != NULL)
  435. PORT_FreeArena (poolp, PR_FALSE);
  436.     return chosen_cipher;
  437. }
  438. /*
  439.  * XXX This is a hack for now to satisfy our current interface.
  440.  * Eventually, with more parameters needing to be specified, just
  441.  * looking up the keysize is not going to be sufficient.
  442.  */
  443. static int
  444. smime_keysize_by_cipher (unsigned long which)
  445. {
  446.     int keysize;
  447.     switch (which) {
  448.       case SMIME_RC2_CBC_40:
  449. keysize = 40;
  450. break;
  451.       case SMIME_RC2_CBC_64:
  452. keysize = 64;
  453. break;
  454.       case SMIME_RC2_CBC_128:
  455. keysize = 128;
  456. break;
  457.       case SMIME_DES_CBC_56:
  458.       case SMIME_DES_EDE3_168:
  459.       case SMIME_FORTEZZA:
  460. /*
  461.  * These are special; since the key size is fixed, we actually
  462.  * want to *avoid* specifying a key size.
  463.  */
  464. keysize = 0;
  465. break;
  466.       default:
  467. keysize = -1;
  468. break;
  469.     }
  470.     return keysize;
  471. }
  472. /*
  473.  * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
  474.  *
  475.  * it would be great for UI purposes if there would be a way to find out which recipients
  476.  * prevented a strong cipher from being used...
  477.  */
  478. SECStatus
  479. NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
  480. {
  481.     unsigned long cipher;
  482.     int mapi;
  483.     cipher = smime_choose_cipher(NULL, rcerts);
  484.     mapi = smime_mapi_by_cipher(cipher);
  485.     *bulkalgtag = smime_cipher_map[mapi].algtag;
  486.     *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].algtag);
  487.     return SECSuccess;
  488. }
  489. /*
  490.  * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
  491.  *
  492.  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
  493.  * S/MIME capabilities attribute value.
  494.  *
  495.  * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
  496.  * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
  497.  *
  498.  * "poolp" - arena pool to create the S/MIME capabilities data on
  499.  * "dest" - SECItem to put the data in
  500.  * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
  501.  */
  502. SECStatus
  503. NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers)
  504. {
  505.     NSSSMIMECapability *cap;
  506.     NSSSMIMECapability **smime_capabilities;
  507.     smime_cipher_map_entry *map;
  508.     SECOidData *oiddata;
  509.     SECItem *dummy;
  510.     int i, capIndex;
  511.     /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
  512.     /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
  513.     smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
  514.       * sizeof(NSSSMIMECapability *));
  515.     if (smime_capabilities == NULL)
  516. return SECFailure;
  517.     capIndex = 0;
  518.     /* Add all the symmetric ciphers
  519.      * We walk the cipher list backwards, as it is ordered by increasing strength,
  520.      * we prefer the stronger cipher over a weaker one, and we have to list the
  521.      * preferred algorithm first */
  522.     for (i = smime_cipher_map_count - 1; i >= 0; i--) {
  523. /* Find the corresponding entry in the cipher map. */
  524. map = &(smime_cipher_map[i]);
  525. if (!map->enabled)
  526.     continue;
  527. /* If we're using a non-Fortezza cert, only advertise non-Fortezza
  528.    capabilities. (We advertise all capabilities if we have a 
  529.    Fortezza cert.) */
  530. if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA))
  531.     continue;
  532. /* get next SMIME capability */
  533. cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
  534. if (cap == NULL)
  535.     break;
  536. smime_capabilities[capIndex++] = cap;
  537. oiddata = SECOID_FindOIDByTag(map->algtag);
  538. if (oiddata == NULL)
  539.     break;
  540. cap->capabilityID.data = oiddata->oid.data;
  541. cap->capabilityID.len = oiddata->oid.len;
  542. cap->parameters.data = map->parms ? map->parms->data : NULL;
  543. cap->parameters.len = map->parms ? map->parms->len : 0;
  544. cap->cipher = smime_cipher_map[i].cipher;
  545.     }
  546.     /* XXX add signature algorithms */
  547.     /* XXX add key encipherment algorithms */
  548.     smime_capabilities[capIndex] = NULL; /* last one - now encode */
  549.     dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
  550.     /* now that we have the proper encoded SMIMECapabilities (or not),
  551.      * free the work data */
  552.     for (i = 0; smime_capabilities[i] != NULL; i++)
  553. PORT_Free(smime_capabilities[i]);
  554.     PORT_Free(smime_capabilities);
  555.     return (dummy == NULL) ? SECFailure : SECSuccess;
  556. }
  557. /*
  558.  * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
  559.  *
  560.  * "poolp" - arena pool to create the attr value on
  561.  * "dest" - SECItem to put the data in
  562.  * "cert" - certificate that should be marked as preferred encryption key
  563.  *          cert is expected to have been verified for EmailRecipient usage.
  564.  */
  565. SECStatus
  566. NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
  567. {
  568.     NSSSMIMEEncryptionKeyPreference ekp;
  569.     SECItem *dummy = NULL;
  570.     PLArenaPool *tmppoolp;
  571.     if (cert == NULL)
  572. goto loser;
  573.     tmppoolp = PORT_NewArena(1024);
  574.     if (tmppoolp == NULL)
  575. goto loser;
  576.     /* XXX hardcoded IssuerSN choice for now */
  577.     ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
  578.     ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
  579.     if (ekp.id.issuerAndSN == NULL)
  580. goto loser;
  581.     dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
  582. loser:
  583.     if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
  584.     return (dummy == NULL) ? SECFailure : SECSuccess;
  585. }
  586. /*
  587.  * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
  588.  * find cert marked by EncryptionKeyPreference attribute
  589.  *
  590.  * "certdb" - handle for the cert database to look in
  591.  * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
  592.  *
  593.  * if certificate is supposed to be found among the message's included certificates,
  594.  * they are assumed to have been imported already.
  595.  */
  596. CERTCertificate *
  597. NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
  598. {
  599.     PLArenaPool *tmppoolp = NULL;
  600.     CERTCertificate *cert = NULL;
  601.     NSSSMIMEEncryptionKeyPreference ekp;
  602.     tmppoolp = PORT_NewArena(1024);
  603.     if (tmppoolp == NULL)
  604. return NULL;
  605.     /* decode DERekp */
  606.     if (SEC_ASN1DecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template, DERekp) != SECSuccess)
  607. goto loser;
  608.     /* find cert */
  609.     switch (ekp.selector) {
  610.     case NSSSMIMEEncryptionKeyPref_IssuerSN:
  611. cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
  612. break;
  613.     case NSSSMIMEEncryptionKeyPref_RKeyID:
  614.     case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
  615. /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
  616. break;
  617.     default:
  618. PORT_Assert(0);
  619.     }
  620. loser:
  621.     if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
  622.     return cert;
  623. }