Back to index

lightning-sunbird  0.9+nobinonly
secmime.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * Stuff specific to S/MIME policy and interoperability.
00039  * Depends on PKCS7, but there should be no dependency the other way around.
00040  *
00041  * $Id: secmime.c,v 1.4 2004/06/18 00:38:45 jpierre%netscape.com Exp $
00042  */
00043 
00044 #include "secmime.h"
00045 #include "secoid.h"
00046 #include "pk11func.h"
00047 #include "ciferfam.h"       /* for CIPHER_FAMILY symbols */
00048 #include "secasn1.h"
00049 #include "secitem.h"
00050 #include "cert.h"
00051 #include "key.h"
00052 #include "secerr.h"
00053 
00054 typedef struct smime_cipher_map_struct {
00055     unsigned long cipher;
00056     SECOidTag algtag;
00057     SECItem *parms;
00058 } smime_cipher_map;
00059 
00060 /*
00061  * These are macros because I think some subsequent parameters,
00062  * like those for RC5, will want to use them, too, separately.
00063  */
00064 #define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10
00065 #define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28
00066 #define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40
00067 #define SMIME_DER_INTVAL_128       SEC_ASN1_INTEGER, 0x02, 0x00, 0x80
00068 
00069 #ifdef SMIME_DOES_RC5       /* will be needed; quiet unused warning for now */
00070 static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };
00071 #endif
00072 static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };
00073 static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };
00074 static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };
00075 
00076 static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };
00077 static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };
00078 static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };
00079 
00080 static smime_cipher_map smime_cipher_maps[] = {
00081     { SMIME_RC2_CBC_40,            SEC_OID_RC2_CBC,     &smime_rc2p40 },
00082     { SMIME_RC2_CBC_64,            SEC_OID_RC2_CBC,     &smime_rc2p64 },
00083     { SMIME_RC2_CBC_128,    SEC_OID_RC2_CBC,     &smime_rc2p128 },
00084 #ifdef SMIME_DOES_RC5
00085     { SMIME_RC5PAD_64_16_40,       SEC_OID_RC5_CBC_PAD, &smime_rc5p40 },
00086     { SMIME_RC5PAD_64_16_64,       SEC_OID_RC5_CBC_PAD, &smime_rc5p64 },
00087     { SMIME_RC5PAD_64_16_128,      SEC_OID_RC5_CBC_PAD, &smime_rc5p128 },
00088 #endif
00089     { SMIME_DES_CBC_56,            SEC_OID_DES_CBC,     NULL },
00090     { SMIME_DES_EDE3_168,   SEC_OID_DES_EDE3_CBC,       NULL },
00091     { SMIME_FORTEZZA,              SEC_OID_FORTEZZA_SKIPJACK, NULL}
00092 };
00093 
00094 /*
00095  * Note, the following value really just needs to be an upper bound
00096  * on the ciphers.
00097  */
00098 static const int smime_symmetric_count = sizeof(smime_cipher_maps)
00099                                     / sizeof(smime_cipher_map);
00100 
00101 static unsigned long *smime_prefs, *smime_newprefs;
00102 static int smime_current_pref_index = 0;
00103 static PRBool smime_prefs_complete = PR_FALSE;
00104 static PRBool smime_prefs_changed = PR_TRUE;
00105 
00106 static unsigned long smime_policy_bits = 0;
00107 
00108 
00109 static int
00110 smime_mapi_by_cipher (unsigned long cipher)
00111 {
00112     int i;
00113 
00114     for (i = 0; i < smime_symmetric_count; i++) {
00115        if (smime_cipher_maps[i].cipher == cipher)
00116            break;
00117     }
00118 
00119     if (i == smime_symmetric_count)
00120        return -1;
00121 
00122     return i;
00123 }
00124 
00125 
00126 /*
00127  * this function locally records the user's preference
00128  */
00129 SECStatus 
00130 SECMIME_EnableCipher(long which, int on)
00131 {
00132     unsigned long mask;
00133 
00134     if (smime_newprefs == NULL || smime_prefs_complete) {
00135        /*
00136         * This is either the very first time, or we are starting over.
00137         */
00138        smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count
00139                                   * sizeof(*smime_newprefs));
00140        if (smime_newprefs == NULL)
00141            return SECFailure;
00142        smime_current_pref_index = 0;
00143        smime_prefs_complete = PR_FALSE;
00144     }
00145 
00146     mask = which & CIPHER_FAMILYID_MASK;
00147     if (mask == CIPHER_FAMILYID_MASK) {
00148        /*
00149         * This call signifies that all preferences have been set.
00150         * Move "newprefs" over, after checking first whether or
00151         * not the new ones are different from the old ones.
00152         */
00153        if (smime_prefs != NULL) {
00154            if (PORT_Memcmp (smime_prefs, smime_newprefs,
00155                           smime_symmetric_count * sizeof(*smime_prefs)) == 0)
00156               smime_prefs_changed = PR_FALSE;
00157            else
00158               smime_prefs_changed = PR_TRUE;
00159            PORT_Free (smime_prefs);
00160        }
00161 
00162        smime_prefs = smime_newprefs;
00163        smime_prefs_complete = PR_TRUE;
00164        return SECSuccess;
00165     }
00166 
00167     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
00168     if (mask != CIPHER_FAMILYID_SMIME) {
00169        /* XXX set an error! */
00170        return SECFailure;
00171     }
00172 
00173     if (on) {
00174        PORT_Assert (smime_current_pref_index < smime_symmetric_count);
00175        if (smime_current_pref_index >= smime_symmetric_count) {
00176            /* XXX set an error! */
00177            return SECFailure;
00178        }
00179 
00180        smime_newprefs[smime_current_pref_index++] = which;
00181     }
00182 
00183     return SECSuccess;
00184 }
00185 
00186 
00187 /*
00188  * this function locally records the export policy
00189  */
00190 SECStatus 
00191 SECMIME_SetPolicy(long which, int on)
00192 {
00193     unsigned long mask;
00194 
00195     PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
00196     if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
00197        /* XXX set an error! */
00198        return SECFailure;
00199     }
00200 
00201     which &= ~CIPHER_FAMILYID_MASK;
00202 
00203     PORT_Assert (which < 32);      /* bits in the long */
00204     if (which >= 32) {
00205        /* XXX set an error! */
00206        return SECFailure;
00207     }
00208 
00209     mask = 1UL << which;
00210 
00211     if (on) {
00212        smime_policy_bits |= mask;
00213     } else {
00214        smime_policy_bits &= ~mask;
00215     }
00216 
00217     return SECSuccess;
00218 }
00219 
00220 
00221 /*
00222  * Based on the given algorithm (including its parameters, in some cases!)
00223  * and the given key (may or may not be inspected, depending on the
00224  * algorithm), find the appropriate policy algorithm specification
00225  * and return it.  If no match can be made, -1 is returned.
00226  */
00227 static long
00228 smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key)
00229 {
00230     SECOidTag algtag;
00231 
00232     algtag = SECOID_GetAlgorithmTag (algid);
00233     switch (algtag) {
00234       case SEC_OID_RC2_CBC:
00235        {
00236            unsigned int keylen_bits;
00237 
00238            keylen_bits = PK11_GetKeyStrength (key, algid);
00239            switch (keylen_bits) {
00240              case 40:
00241               return SMIME_RC2_CBC_40;
00242              case 64:
00243               return SMIME_RC2_CBC_64;
00244              case 128:
00245               return SMIME_RC2_CBC_128;
00246              default:
00247               break;
00248            }
00249        }
00250        break;
00251       case SEC_OID_DES_CBC:
00252        return SMIME_DES_CBC_56;
00253       case SEC_OID_DES_EDE3_CBC:
00254        return SMIME_DES_EDE3_168;
00255       case SEC_OID_FORTEZZA_SKIPJACK:
00256        return SMIME_FORTEZZA;
00257 #ifdef SMIME_DOES_RC5
00258       case SEC_OID_RC5_CBC_PAD:
00259        PORT_Assert (0);     /* XXX need to pull out parameters and match */
00260        break;
00261 #endif
00262       default:
00263        break;
00264     }
00265 
00266     return -1;
00267 }
00268 
00269 
00270 static PRBool
00271 smime_cipher_allowed (unsigned long which)
00272 {
00273     unsigned long mask;
00274 
00275     which &= ~CIPHER_FAMILYID_MASK;
00276     PORT_Assert (which < 32);      /* bits per long (min) */
00277     if (which >= 32)
00278        return PR_FALSE;
00279 
00280     mask = 1UL << which;
00281     if ((mask & smime_policy_bits) == 0)
00282        return PR_FALSE;
00283 
00284     return PR_TRUE;
00285 }
00286 
00287 
00288 PRBool
00289 SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
00290 {
00291     long which;
00292 
00293     which = smime_policy_algorithm (algid, key);
00294     if (which < 0)
00295        return PR_FALSE;
00296 
00297     return smime_cipher_allowed ((unsigned long)which);
00298 }
00299 
00300 
00301 /*
00302  * Does the current policy allow *any* S/MIME encryption (or decryption)?
00303  *
00304  * This tells whether or not *any* S/MIME encryption can be done,
00305  * according to policy.  Callers may use this to do nicer user interface
00306  * (say, greying out a checkbox so a user does not even try to encrypt
00307  * a message when they are not allowed to) or for any reason they want
00308  * to check whether S/MIME encryption (or decryption, for that matter)
00309  * may be done.
00310  *
00311  * It takes no arguments.  The return value is a simple boolean:
00312  *   PR_TRUE means encryption (or decryption) is *possible*
00313  *     (but may still fail due to other reasons, like because we cannot
00314  *     find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
00315  *   PR_FALSE means encryption (or decryption) is not permitted
00316  *
00317  * There are no errors from this routine.
00318  */
00319 PRBool
00320 SECMIME_EncryptionPossible (void)
00321 {
00322     if (smime_policy_bits != 0)
00323        return PR_TRUE;
00324 
00325     return PR_FALSE;
00326 }
00327 
00328 
00329 /*
00330  * XXX Would like the "parameters" field to be a SECItem *, but the
00331  * encoder is having trouble with optional pointers to an ANY.  Maybe
00332  * once that is fixed, can change this back...
00333  */
00334 typedef struct smime_capability_struct {
00335     unsigned long cipher;   /* local; not part of encoding */
00336     SECOidTag capIDTag;            /* local; not part of encoding */
00337     SECItem capabilityID;
00338     SECItem parameters;
00339 } smime_capability;
00340 
00341 static const SEC_ASN1Template smime_capability_template[] = {
00342     { SEC_ASN1_SEQUENCE,
00343          0, NULL, sizeof(smime_capability) },
00344     { SEC_ASN1_OBJECT_ID,
00345          offsetof(smime_capability,capabilityID), },
00346     { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
00347          offsetof(smime_capability,parameters), },
00348     { 0, }
00349 };
00350 
00351 static const SEC_ASN1Template smime_capabilities_template[] = {
00352     { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
00353 };
00354 
00355 
00356 
00357 static void
00358 smime_fill_capability (smime_capability *cap)
00359 {
00360     unsigned long cipher;
00361     SECOidTag algtag;
00362     int i;
00363 
00364     algtag = SECOID_FindOIDTag (&(cap->capabilityID));
00365 
00366     for (i = 0; i < smime_symmetric_count; i++) {
00367        if (smime_cipher_maps[i].algtag != algtag)
00368            continue;
00369        /*
00370         * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
00371         * 2 NULLs as equal and NULL and non-NULL as not equal), we could
00372         * use that here instead of all of the following comparison code.
00373         */
00374        if (cap->parameters.data != NULL) {
00375            if (smime_cipher_maps[i].parms == NULL)
00376               continue;
00377            if (cap->parameters.len != smime_cipher_maps[i].parms->len)
00378               continue;
00379            if (PORT_Memcmp (cap->parameters.data,
00380                           smime_cipher_maps[i].parms->data,
00381                           cap->parameters.len) == 0)
00382               break;
00383        } else if (smime_cipher_maps[i].parms == NULL) {
00384            break;
00385        }
00386     }
00387 
00388     if (i == smime_symmetric_count)
00389        cipher = 0;
00390     else
00391        cipher = smime_cipher_maps[i].cipher;
00392 
00393     cap->cipher = cipher;
00394     cap->capIDTag = algtag;
00395 }
00396 
00397 
00398 static long
00399 smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts)
00400 {
00401     PRArenaPool *poolp;
00402     long chosen_cipher;
00403     int *cipher_abilities;
00404     int *cipher_votes;
00405     int strong_mapi;
00406     int rcount, mapi, max, i;
00407        PRBool isFortezza = PK11_FortezzaHasKEA(scert);
00408 
00409     if (smime_policy_bits == 0) {
00410        PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
00411        return -1;
00412     }
00413 
00414     chosen_cipher = SMIME_RC2_CBC_40;            /* the default, LCD */
00415 
00416     poolp = PORT_NewArena (1024);         /* XXX what is right value? */
00417     if (poolp == NULL)
00418        goto done;
00419 
00420     cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,
00421                                     smime_symmetric_count * sizeof(int));
00422     if (cipher_abilities == NULL)
00423        goto done;
00424 
00425     cipher_votes = (int*)PORT_ArenaZAlloc (poolp,
00426                                  smime_symmetric_count * sizeof(int));
00427     if (cipher_votes == NULL)
00428        goto done;
00429 
00430     /*
00431      * XXX Should have a #define somewhere which specifies default
00432      * strong cipher.  (Or better, a way to configure, which would
00433      * take Fortezza into account as well.)
00434      */
00435 
00436     /* If the user has the Fortezza preference turned on, make
00437      *  that the strong cipher. Otherwise, use triple-DES. */
00438     strong_mapi = -1;
00439     if (isFortezza) {
00440        for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++)
00441        {
00442            if (smime_prefs[i] == SMIME_FORTEZZA)
00443               strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
00444        }
00445     }
00446 
00447     if (strong_mapi == -1)
00448        strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
00449 
00450     PORT_Assert (strong_mapi >= 0);
00451 
00452     for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
00453        SECItem *profile;
00454        smime_capability **caps;
00455        int capi, pref;
00456        SECStatus dstat;
00457 
00458        pref = smime_symmetric_count;
00459        profile = CERT_FindSMimeProfile (rcerts[rcount]);
00460        if (profile != NULL && profile->data != NULL && profile->len > 0) {
00461            caps = NULL;
00462            dstat = SEC_QuickDERDecodeItem (poolp, &caps,
00463                                    smime_capabilities_template,
00464                                    profile);
00465            if (dstat == SECSuccess && caps != NULL) {
00466               for (capi = 0; caps[capi] != NULL; capi++) {
00467                   smime_fill_capability (caps[capi]);
00468                   mapi = smime_mapi_by_cipher (caps[capi]->cipher);
00469                   if (mapi >= 0) {
00470                      cipher_abilities[mapi]++;
00471                      cipher_votes[mapi] += pref;
00472                      --pref;
00473                   }
00474               }
00475            }
00476        } else {
00477            SECKEYPublicKey *key;
00478            unsigned int pklen_bits;
00479 
00480            /*
00481             * XXX This is probably only good for RSA keys.  What I would
00482             * really like is a function to just say;  Is the public key in
00483             * this cert an export-length key?  Then I would not have to
00484             * know things like the value 512, or the kind of key, or what
00485             * a subjectPublicKeyInfo is, etc.
00486             */
00487            key = CERT_ExtractPublicKey (rcerts[rcount]);
00488            if (key != NULL) {
00489               pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
00490               SECKEY_DestroyPublicKey (key);
00491 
00492               if (pklen_bits > 512) {
00493                   cipher_abilities[strong_mapi]++;
00494                   cipher_votes[strong_mapi] += pref;
00495               }
00496            }
00497        }
00498        if (profile != NULL)
00499            SECITEM_FreeItem (profile, PR_TRUE);
00500     }
00501 
00502     max = 0;
00503     for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
00504        if (cipher_abilities[mapi] != rcount)
00505            continue;
00506        if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher))
00507            continue;
00508        if (!isFortezza  && (smime_cipher_maps[mapi].cipher == SMIME_FORTEZZA))
00509               continue;
00510        if (cipher_votes[mapi] > max) {
00511            chosen_cipher = smime_cipher_maps[mapi].cipher;
00512            max = cipher_votes[mapi];
00513        } /* XXX else if a tie, let scert break it? */
00514     }
00515 
00516 done:
00517     if (poolp != NULL)
00518        PORT_FreeArena (poolp, PR_FALSE);
00519 
00520     return chosen_cipher;
00521 }
00522 
00523 
00524 /*
00525  * XXX This is a hack for now to satisfy our current interface.
00526  * Eventually, with more parameters needing to be specified, just
00527  * looking up the keysize is not going to be sufficient.
00528  */
00529 static int
00530 smime_keysize_by_cipher (unsigned long which)
00531 {
00532     int keysize;
00533 
00534     switch (which) {
00535       case SMIME_RC2_CBC_40:
00536        keysize = 40;
00537        break;
00538       case SMIME_RC2_CBC_64:
00539        keysize = 64;
00540        break;
00541       case SMIME_RC2_CBC_128:
00542        keysize = 128;
00543        break;
00544 #ifdef SMIME_DOES_RC5
00545       case SMIME_RC5PAD_64_16_40:
00546       case SMIME_RC5PAD_64_16_64:
00547       case SMIME_RC5PAD_64_16_128:
00548        /* XXX See comment above; keysize is not enough... */
00549        PORT_Assert (0);
00550        PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
00551        keysize = -1;
00552        break;
00553 #endif
00554       case SMIME_DES_CBC_56:
00555       case SMIME_DES_EDE3_168:
00556       case SMIME_FORTEZZA:
00557        /*
00558         * These are special; since the key size is fixed, we actually
00559         * want to *avoid* specifying a key size.
00560         */
00561        keysize = 0;
00562        break;
00563       default:
00564        keysize = -1;
00565        break;
00566     }
00567 
00568     return keysize;
00569 }
00570 
00571 
00572 /*
00573  * Start an S/MIME encrypting context.
00574  *
00575  * "scert" is the cert for the sender.  It will be checked for validity.
00576  * "rcerts" are the certs for the recipients.  They will also be checked.
00577  *
00578  * "certdb" is the cert database to use for verifying the certs.
00579  * It can be NULL if a default database is available (like in the client).
00580  *
00581  * This function already does all of the stuff specific to S/MIME protocol
00582  * and local policy; the return value just needs to be passed to
00583  * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
00584  * and finally to SEC_PKCS7DestroyContentInfo().
00585  *
00586  * An error results in a return value of NULL and an error set.
00587  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
00588  */
00589 SEC_PKCS7ContentInfo *
00590 SECMIME_CreateEncrypted(CERTCertificate *scert,
00591                      CERTCertificate **rcerts,
00592                      CERTCertDBHandle *certdb,
00593                      SECKEYGetPasswordKey pwfn,
00594                      void *pwfn_arg)
00595 {
00596     SEC_PKCS7ContentInfo *cinfo;
00597     long cipher;
00598     SECOidTag encalg;
00599     int keysize;
00600     int mapi, rci;
00601 
00602     cipher = smime_choose_cipher (scert, rcerts);
00603     if (cipher < 0)
00604        return NULL;
00605 
00606     mapi = smime_mapi_by_cipher (cipher);
00607     if (mapi < 0)
00608        return NULL;
00609 
00610     /*
00611      * XXX This is stretching it -- CreateEnvelopedData should probably
00612      * take a cipher itself of some sort, because we cannot know what the
00613      * future will bring in terms of parameters for each type of algorithm.
00614      * For example, just an algorithm and keysize is *not* sufficient to
00615      * fully specify the usage of RC5 (which also needs to know rounds and
00616      * block size).  Work this out into a better API!
00617      */
00618     encalg = smime_cipher_maps[mapi].algtag;
00619     keysize = smime_keysize_by_cipher (cipher);
00620     if (keysize < 0)
00621        return NULL;
00622 
00623     cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
00624                                      certdb, encalg, keysize,
00625                                      pwfn, pwfn_arg);
00626     if (cinfo == NULL)
00627        return NULL;
00628 
00629     for (rci = 0; rcerts[rci] != NULL; rci++) {
00630        if (rcerts[rci] == scert)
00631            continue;
00632        if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
00633                                NULL) != SECSuccess) {
00634            SEC_PKCS7DestroyContentInfo (cinfo);
00635            return NULL;
00636        }
00637     }
00638 
00639     return cinfo;
00640 }
00641 
00642 
00643 static smime_capability **smime_capabilities;
00644 static SECItem *smime_encoded_caps;
00645 static PRBool lastUsedFortezza;
00646 
00647 
00648 static SECStatus
00649 smime_init_caps (PRBool isFortezza)
00650 {
00651     smime_capability *cap;
00652     smime_cipher_map *map;
00653     SECOidData *oiddata;
00654     SECStatus rv;
00655     int i, capIndex;
00656 
00657     if (smime_encoded_caps != NULL 
00658        && (! smime_prefs_changed) 
00659        && lastUsedFortezza == isFortezza)
00660        return SECSuccess;
00661 
00662     if (smime_encoded_caps != NULL) {
00663        SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
00664        smime_encoded_caps = NULL;
00665     }
00666 
00667     if (smime_capabilities == NULL) {
00668        smime_capabilities = (smime_capability**)PORT_ZAlloc (
00669                                      (smime_symmetric_count + 1)
00670                                      * sizeof(smime_capability *));
00671        if (smime_capabilities == NULL)
00672            return SECFailure;
00673     }
00674 
00675     rv = SECFailure;
00676 
00677     /* 
00678        The process of creating the encoded PKCS7 cipher capability list
00679        involves two basic steps: 
00680 
00681        (a) Convert our internal representation of cipher preferences 
00682            (smime_prefs) into an array containing cipher OIDs and 
00683           parameter data (smime_capabilities). This step is
00684           performed here.
00685 
00686        (b) Encode, using ASN.1, the cipher information in 
00687            smime_capabilities, leaving the encoded result in 
00688           smime_encoded_caps.
00689 
00690        (In the process of performing (a), Lisa put in some optimizations
00691        which allow us to avoid needlessly re-populating elements in 
00692        smime_capabilities as we walk through smime_prefs.)
00693 
00694        We want to use separate loop variables for smime_prefs and
00695        smime_capabilities because in the case where the Skipjack cipher 
00696        is turned on in the prefs, but where we don't want to include 
00697        Skipjack in the encoded capabilities (presumably due to using a 
00698        non-fortezza cert when sending a message), we want to avoid creating
00699        an empty element in smime_capabilities. This would otherwise cause 
00700        the encoding step to produce an empty set, since Skipjack happens 
00701        to be the first cipher in smime_prefs, if it is turned on.
00702     */
00703     for (i = 0, capIndex = 0; i < smime_current_pref_index; i++, capIndex++) {
00704        int mapi;
00705 
00706        /* Get the next cipher preference in smime_prefs. */
00707        mapi = smime_mapi_by_cipher (smime_prefs[i]);
00708        if (mapi < 0)
00709            break;
00710 
00711        /* Find the corresponding entry in the cipher map. */
00712        PORT_Assert (mapi < smime_symmetric_count);
00713        map = &(smime_cipher_maps[mapi]);
00714 
00715        /* If we're using a non-Fortezza cert, only advertise non-Fortezza
00716           capabilities. (We advertise all capabilities if we have a 
00717           Fortezza cert.) */
00718        if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA))
00719        {
00720            capIndex--; /* we want to visit the same caps index entry next time */
00721            continue;
00722        }
00723 
00724        /*
00725         * Convert the next preference found in smime_prefs into an
00726         * smime_capability.
00727         */
00728 
00729        cap = smime_capabilities[capIndex];
00730        if (cap == NULL) {
00731            cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability));
00732            if (cap == NULL)
00733               break;
00734            smime_capabilities[capIndex] = cap;
00735        } else if (cap->cipher == smime_prefs[i]) {
00736            continue;        /* no change to this one */
00737        }
00738 
00739        cap->capIDTag = map->algtag;
00740        oiddata = SECOID_FindOIDByTag (map->algtag);
00741        if (oiddata == NULL)
00742            break;
00743 
00744        if (cap->capabilityID.data != NULL) {
00745            SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
00746            cap->capabilityID.data = NULL;
00747            cap->capabilityID.len = 0;
00748        }
00749 
00750        rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid));
00751        if (rv != SECSuccess)
00752            break;
00753 
00754        if (map->parms == NULL) {
00755            cap->parameters.data = NULL;
00756            cap->parameters.len = 0;
00757        } else {
00758            cap->parameters.data = map->parms->data;
00759            cap->parameters.len = map->parms->len;
00760        }
00761 
00762        cap->cipher = smime_prefs[i];
00763     }
00764 
00765     if (i != smime_current_pref_index)
00766        return rv;
00767 
00768     while (capIndex < smime_symmetric_count) {
00769        cap = smime_capabilities[capIndex];
00770        if (cap != NULL) {
00771            SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
00772            PORT_Free (cap);
00773        }
00774        smime_capabilities[capIndex] = NULL;
00775        capIndex++;
00776     }
00777     smime_capabilities[capIndex] = NULL;
00778 
00779     smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
00780                                         smime_capabilities_template);
00781     if (smime_encoded_caps == NULL)
00782        return SECFailure;
00783 
00784     lastUsedFortezza = isFortezza;
00785 
00786     return SECSuccess;
00787 }
00788 
00789 
00790 static SECStatus
00791 smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
00792 {
00793     PRBool isFortezza = PR_FALSE;
00794 
00795     PORT_Assert (smime_prefs_complete);
00796     if (! smime_prefs_complete)
00797        return SECFailure;
00798 
00799     /* See if the sender's cert specifies Fortezza key exchange. */
00800     if (cert != NULL)
00801        isFortezza = PK11_FortezzaHasKEA(cert);
00802 
00803     /* For that matter, if capabilities haven't been initialized yet,
00804        do so now. */
00805     if (isFortezza != lastUsedFortezza || smime_encoded_caps == NULL || smime_prefs_changed) {
00806        SECStatus rv;
00807 
00808        rv = smime_init_caps(isFortezza);
00809        if (rv != SECSuccess)
00810            return rv;
00811 
00812        PORT_Assert (smime_encoded_caps != NULL);
00813     }
00814 
00815     return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
00816                                    smime_encoded_caps);
00817 }
00818 
00819 
00820 /*
00821  * Start an S/MIME signing context.
00822  *
00823  * "scert" is the cert that will be used to sign the data.  It will be
00824  * checked for validity.
00825  *
00826  * "ecert" is the signer's encryption cert.  If it is different from
00827  * scert, then it will be included in the signed message so that the
00828  * recipient can save it for future encryptions.
00829  *
00830  * "certdb" is the cert database to use for verifying the cert.
00831  * It can be NULL if a default database is available (like in the client).
00832  * 
00833  * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
00834  * XXX There should be SECMIME functions for hashing, or the hashing should
00835  * be built into this interface, which we would like because we would
00836  * support more smartcards that way, and then this argument should go away.)
00837  *
00838  * "digest" is the actual digest of the data.  It must be provided in
00839  * the case of detached data or NULL if the content will be included.
00840  *
00841  * This function already does all of the stuff specific to S/MIME protocol
00842  * and local policy; the return value just needs to be passed to
00843  * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
00844  * and finally to SEC_PKCS7DestroyContentInfo().
00845  *
00846  * An error results in a return value of NULL and an error set.
00847  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
00848  */
00849 
00850 SEC_PKCS7ContentInfo *
00851 SECMIME_CreateSigned (CERTCertificate *scert,
00852                     CERTCertificate *ecert,
00853                     CERTCertDBHandle *certdb,
00854                     SECOidTag digestalg,
00855                     SECItem *digest,
00856                     SECKEYGetPasswordKey pwfn,
00857                     void *pwfn_arg)
00858 {
00859     SEC_PKCS7ContentInfo *cinfo;
00860     SECStatus rv;
00861 
00862     /* See note in header comment above about digestalg. */
00863     /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */
00864 
00865     cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner,
00866                                    certdb, digestalg, digest,
00867                                    pwfn, pwfn_arg);
00868     if (cinfo == NULL)
00869        return NULL;
00870 
00871     if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) {
00872        SEC_PKCS7DestroyContentInfo (cinfo);
00873        return NULL;
00874     }
00875 
00876     /* if the encryption cert and the signing cert differ, then include
00877      * the encryption cert too.
00878      */
00879     /* it is ok to compare the pointers since we ref count, and the same
00880      * cert will always have the same pointer
00881      */
00882     if ( ( ecert != NULL ) && ( ecert != scert ) ) {
00883        rv = SEC_PKCS7AddCertificate(cinfo, ecert);
00884        if ( rv != SECSuccess ) {
00885            SEC_PKCS7DestroyContentInfo (cinfo);
00886            return NULL;
00887        }
00888     }
00889     /*
00890      * Add the signing time.  But if it fails for some reason,
00891      * may as well not give up altogether -- just assert.
00892      */
00893     rv = SEC_PKCS7AddSigningTime (cinfo);
00894     PORT_Assert (rv == SECSuccess);
00895 
00896     /*
00897      * Add the email profile.  Again, if it fails for some reason,
00898      * may as well not give up altogether -- just assert.
00899      */
00900     rv = smime_add_profile (ecert, cinfo);
00901     PORT_Assert (rv == SECSuccess);
00902 
00903     return cinfo;
00904 }