Back to index

lightning-sunbird  0.9+nobinonly
smimeutil.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  *
00040  * $Id: smimeutil.c,v 1.16.28.5 2007/05/07 22:46:06 nelson%bolyard.com Exp $
00041  */
00042 
00043 #include "secmime.h"
00044 #include "secoid.h"
00045 #include "pk11func.h"
00046 #include "ciferfam.h"       /* for CIPHER_FAMILY symbols */
00047 #include "secasn1.h"
00048 #include "secitem.h"
00049 #include "cert.h"
00050 #include "key.h"
00051 #include "secerr.h"
00052 #include "cms.h"
00053 #include "nss.h"
00054 
00055 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
00056 SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
00057 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
00058 
00059 /* various integer's ASN.1 encoding */
00060 static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
00061 static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
00062 static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
00063 
00064 /* RC2 algorithm parameters (used in smime_cipher_map) */
00065 static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
00066 static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
00067 static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
00068 
00069 /*
00070  * XXX Would like the "parameters" field to be a SECItem *, but the
00071  * encoder is having trouble with optional pointers to an ANY.  Maybe
00072  * once that is fixed, can change this back...
00073  */
00074 typedef struct {
00075     SECItem capabilityID;
00076     SECItem parameters;
00077     long cipher;            /* optimization */
00078 } NSSSMIMECapability;
00079 
00080 static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
00081     { SEC_ASN1_SEQUENCE,
00082          0, NULL, sizeof(NSSSMIMECapability) },
00083     { SEC_ASN1_OBJECT_ID,
00084          offsetof(NSSSMIMECapability,capabilityID), },
00085     { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
00086          offsetof(NSSSMIMECapability,parameters), },
00087     { 0, }
00088 };
00089 
00090 static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
00091     { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
00092 };
00093 
00094 /*
00095  * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
00096  *  to store this and only this certificate permanently for the sender email address.
00097  */
00098 typedef enum {
00099     NSSSMIMEEncryptionKeyPref_IssuerSN,
00100     NSSSMIMEEncryptionKeyPref_RKeyID,
00101     NSSSMIMEEncryptionKeyPref_SubjectKeyID
00102 } NSSSMIMEEncryptionKeyPrefSelector;
00103 
00104 typedef struct {
00105     NSSSMIMEEncryptionKeyPrefSelector selector;
00106     union {
00107        CERTIssuerAndSN                    *issuerAndSN;
00108        NSSCMSRecipientKeyIdentifier       *recipientKeyID;
00109        SECItem                            *subjectKeyID;
00110     } id;
00111 } NSSSMIMEEncryptionKeyPreference;
00112 
00113 extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
00114 
00115 static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
00116     { SEC_ASN1_CHOICE,
00117          offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
00118          sizeof(NSSSMIMEEncryptionKeyPreference) },
00119     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0
00120           | SEC_ASN1_CONSTRUCTED,
00121          offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
00122          SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
00123          NSSSMIMEEncryptionKeyPref_IssuerSN },
00124     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1
00125           | SEC_ASN1_CONSTRUCTED,
00126          offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
00127          NSSCMSRecipientKeyIdentifierTemplate,
00128          NSSSMIMEEncryptionKeyPref_RKeyID },
00129     { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2
00130           | SEC_ASN1_CONSTRUCTED,
00131          offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
00132          SEC_ASN1_SUB(SEC_OctetStringTemplate),
00133          NSSSMIMEEncryptionKeyPref_SubjectKeyID },
00134     { 0, }
00135 };
00136 
00137 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
00138 typedef struct {
00139     unsigned long cipher;
00140     SECOidTag algtag;
00141     SECItem *parms;
00142     PRBool enabled;  /* in the user's preferences */
00143     PRBool allowed;  /* per export policy */
00144 } smime_cipher_map_entry;
00145 
00146 /* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
00147 static smime_cipher_map_entry smime_cipher_map[] = {
00148 /*    cipher                algtag               parms         enabled  allowed */
00149 /*    ---------------------------------------------------------------------------------- */
00150     { SMIME_RC2_CBC_40,            SEC_OID_RC2_CBC,     &param_int40, PR_TRUE, PR_TRUE },
00151     { SMIME_DES_CBC_56,            SEC_OID_DES_CBC,     NULL,         PR_TRUE, PR_TRUE },
00152     { SMIME_RC2_CBC_64,            SEC_OID_RC2_CBC,     &param_int64, PR_TRUE, PR_TRUE },
00153     { SMIME_RC2_CBC_128,    SEC_OID_RC2_CBC,     &param_int128,       PR_TRUE, PR_TRUE },
00154     { SMIME_DES_EDE3_168,   SEC_OID_DES_EDE3_CBC,       NULL,         PR_TRUE, PR_TRUE },
00155     { SMIME_FORTEZZA,              SEC_OID_FORTEZZA_SKIPJACK, NULL,   PR_TRUE, PR_TRUE }
00156 };
00157 static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
00158 
00159 /*
00160  * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
00161  */
00162 static int
00163 smime_mapi_by_cipher(unsigned long cipher)
00164 {
00165     int i;
00166 
00167     for (i = 0; i < smime_cipher_map_count; i++) {
00168        if (smime_cipher_map[i].cipher == cipher)
00169            return i; /* bingo */
00170     }
00171     return -1;              /* should not happen if we're consistent, right? */
00172 }
00173 
00174 /*
00175  * NSS_SMIME_EnableCipher - this function locally records the user's preference
00176  */
00177 SECStatus 
00178 NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
00179 {
00180     unsigned long mask;
00181     int mapi;
00182 
00183     mask = which & CIPHER_FAMILYID_MASK;
00184 
00185     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
00186     if (mask != CIPHER_FAMILYID_SMIME)
00187        /* XXX set an error! */
00188        return SECFailure;
00189 
00190     mapi = smime_mapi_by_cipher(which);
00191     if (mapi < 0)
00192        /* XXX set an error */
00193        return SECFailure;
00194 
00195     /* do we try to turn on a forbidden cipher? */
00196     if (!smime_cipher_map[mapi].allowed && on) {
00197        PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
00198        return SECFailure;
00199     }
00200 
00201     if (smime_cipher_map[mapi].enabled != on)
00202        smime_cipher_map[mapi].enabled = on;
00203 
00204     return SECSuccess;
00205 }
00206 
00207 
00208 /*
00209  * this function locally records the export policy
00210  */
00211 SECStatus 
00212 NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
00213 {
00214     unsigned long mask;
00215     int mapi;
00216 
00217     mask = which & CIPHER_FAMILYID_MASK;
00218 
00219     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
00220     if (mask != CIPHER_FAMILYID_SMIME)
00221        /* XXX set an error! */
00222        return SECFailure;
00223 
00224     mapi = smime_mapi_by_cipher(which);
00225     if (mapi < 0)
00226        /* XXX set an error */
00227        return SECFailure;
00228 
00229     if (smime_cipher_map[mapi].allowed != on)
00230        smime_cipher_map[mapi].allowed = on;
00231 
00232     return SECSuccess;
00233 }
00234 
00235 /*
00236  * Based on the given algorithm (including its parameters, in some cases!)
00237  * and the given key (may or may not be inspected, depending on the
00238  * algorithm), find the appropriate policy algorithm specification
00239  * and return it.  If no match can be made, -1 is returned.
00240  */
00241 static SECStatus
00242 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
00243 {
00244     SECOidTag algtag;
00245     unsigned int keylen_bits;
00246     unsigned long c;
00247 
00248     algtag = SECOID_GetAlgorithmTag(algid);
00249     switch (algtag) {
00250     case SEC_OID_RC2_CBC:
00251        keylen_bits = PK11_GetKeyStrength(key, algid);
00252        switch (keylen_bits) {
00253        case 40:
00254            c = SMIME_RC2_CBC_40;
00255            break;
00256        case 64:
00257            c = SMIME_RC2_CBC_64;
00258            break;
00259        case 128:
00260            c = SMIME_RC2_CBC_128;
00261            break;
00262        default:
00263            return SECFailure;
00264        }
00265        break;
00266     case SEC_OID_DES_CBC:
00267        c = SMIME_DES_CBC_56;
00268        break;
00269     case SEC_OID_DES_EDE3_CBC:
00270        c = SMIME_DES_EDE3_168;
00271        break;
00272     case SEC_OID_FORTEZZA_SKIPJACK:
00273        c = SMIME_FORTEZZA;
00274        break;
00275     default:
00276        return SECFailure;
00277     }
00278     *cipher = c;
00279     return SECSuccess;
00280 }
00281 
00282 static PRBool
00283 nss_smime_cipher_allowed(unsigned long which)
00284 {
00285     int mapi;
00286 
00287     mapi = smime_mapi_by_cipher(which);
00288     if (mapi < 0)
00289        return PR_FALSE;
00290     return smime_cipher_map[mapi].allowed;
00291 }
00292 
00293 PRBool
00294 NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
00295 {
00296     unsigned long which;
00297 
00298     if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
00299        return PR_FALSE;
00300 
00301     return nss_smime_cipher_allowed(which);
00302 }
00303 
00304 
00305 /*
00306  * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
00307  *
00308  * This tells whether or not *any* S/MIME encryption can be done,
00309  * according to policy.  Callers may use this to do nicer user interface
00310  * (say, greying out a checkbox so a user does not even try to encrypt
00311  * a message when they are not allowed to) or for any reason they want
00312  * to check whether S/MIME encryption (or decryption, for that matter)
00313  * may be done.
00314  *
00315  * It takes no arguments.  The return value is a simple boolean:
00316  *   PR_TRUE means encryption (or decryption) is *possible*
00317  *     (but may still fail due to other reasons, like because we cannot
00318  *     find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
00319  *   PR_FALSE means encryption (or decryption) is not permitted
00320  *
00321  * There are no errors from this routine.
00322  */
00323 PRBool
00324 NSS_SMIMEUtil_EncryptionPossible(void)
00325 {
00326     int i;
00327 
00328     for (i = 0; i < smime_cipher_map_count; i++) {
00329        if (smime_cipher_map[i].allowed)
00330            return PR_TRUE;
00331     }
00332     return PR_FALSE;
00333 }
00334 
00335 
00336 static int
00337 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
00338 {
00339     int i;
00340     SECOidTag capIDTag;
00341 
00342     /* we need the OIDTag here */
00343     capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
00344 
00345     /* go over all the SMIME ciphers we know and see if we find a match */
00346     for (i = 0; i < smime_cipher_map_count; i++) {
00347        if (smime_cipher_map[i].algtag != capIDTag)
00348            continue;
00349        /*
00350         * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
00351         * 2 NULLs as equal and NULL and non-NULL as not equal), we could
00352         * use that here instead of all of the following comparison code.
00353         */
00354        if (!smime_cipher_map[i].parms) { 
00355            if (!cap->parameters.data || !cap->parameters.len)
00356               break; /* both empty: bingo */
00357            if (cap->parameters.len     == 2  &&
00358                cap->parameters.data[0] == SEC_ASN1_NULL &&
00359               cap->parameters.data[1] == 0) 
00360               break;  /* DER NULL == NULL, bingo */
00361        } else if (cap->parameters.data != NULL && 
00362            cap->parameters.len == smime_cipher_map[i].parms->len &&
00363            PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
00364                           cap->parameters.len) == 0)
00365        {
00366            break;    /* both not empty, same length & equal content: bingo */
00367        }
00368     }
00369 
00370     if (i == smime_cipher_map_count)
00371        return 0;                          /* no match found */
00372     return smime_cipher_map[i].cipher;    /* match found, point to cipher */
00373 }
00374 
00375 /*
00376  * smime_choose_cipher - choose a cipher that works for all the recipients
00377  *
00378  * "scert"  - sender's certificate
00379  * "rcerts" - recipient's certificates
00380  */
00381 static long
00382 smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
00383 {
00384     PRArenaPool *poolp;
00385     long cipher;
00386     long chosen_cipher;
00387     int *cipher_abilities;
00388     int *cipher_votes;
00389     int weak_mapi;
00390     int strong_mapi;
00391     int rcount, mapi, max, i;
00392     PRBool scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
00393 
00394     chosen_cipher = SMIME_RC2_CBC_40;            /* the default, LCD */
00395     weak_mapi = smime_mapi_by_cipher(chosen_cipher);
00396 
00397     poolp = PORT_NewArena (1024);         /* XXX what is right value? */
00398     if (poolp == NULL)
00399        goto done;
00400 
00401     cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
00402     cipher_votes     = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
00403     if (cipher_votes == NULL || cipher_abilities == NULL)
00404        goto done;
00405 
00406     /* If the user has the Fortezza preference turned on, make
00407      *  that the strong cipher. Otherwise, use triple-DES. */
00408     strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
00409     if (scert_is_fortezza) {
00410        mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
00411        if (mapi >= 0 && smime_cipher_map[mapi].enabled)
00412            strong_mapi = mapi;
00413     }
00414 
00415     /* walk all the recipient's certs */
00416     for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
00417        SECItem *profile;
00418        NSSSMIMECapability **caps;
00419        int pref;
00420 
00421        /* the first cipher that matches in the user's SMIME profile gets
00422         * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
00423         * and so on. If every cipher matches, the last one gets 1 (one) vote */
00424        pref = smime_cipher_map_count;
00425 
00426        /* find recipient's SMIME profile */
00427        profile = CERT_FindSMimeProfile(rcerts[rcount]);
00428 
00429        if (profile != NULL && profile->data != NULL && profile->len > 0) {
00430            /* we have a profile (still DER-encoded) */
00431            caps = NULL;
00432            /* decode it */
00433            if (SEC_QuickDERDecodeItem(poolp, &caps,
00434                     NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
00435                   caps != NULL)
00436            {
00437               /* walk the SMIME capabilities for this recipient */
00438               for (i = 0; caps[i] != NULL; i++) {
00439                   cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
00440                   mapi = smime_mapi_by_cipher(cipher);
00441                   if (mapi >= 0) {
00442                      /* found the cipher */
00443                      cipher_abilities[mapi]++;
00444                      cipher_votes[mapi] += pref;
00445                      --pref;
00446                   }
00447               }
00448            }
00449        } else {
00450            /* no profile found - so we can only assume that the user can do
00451             * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
00452            SECKEYPublicKey *key;
00453            unsigned int pklen_bits;
00454 
00455            /*
00456             * if recipient's public key length is > 512, vote for a strong cipher
00457             * please not that the side effect of this is that if only one recipient
00458             * has an export-level public key, the strong cipher is disabled.
00459             *
00460             * XXX This is probably only good for RSA keys.  What I would
00461             * really like is a function to just say;  Is the public key in
00462             * this cert an export-length key?  Then I would not have to
00463             * know things like the value 512, or the kind of key, or what
00464             * a subjectPublicKeyInfo is, etc.
00465             */
00466            key = CERT_ExtractPublicKey(rcerts[rcount]);
00467            pklen_bits = 0;
00468            if (key != NULL) {
00469               pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
00470               SECKEY_DestroyPublicKey (key);
00471            }
00472 
00473            if (pklen_bits > 512) {
00474               /* cast votes for the strong algorithm */
00475               cipher_abilities[strong_mapi]++;
00476               cipher_votes[strong_mapi] += pref;
00477               pref--;
00478            } 
00479 
00480            /* always cast (possibly less) votes for the weak algorithm */
00481            cipher_abilities[weak_mapi]++;
00482            cipher_votes[weak_mapi] += pref;
00483        }
00484        if (profile != NULL)
00485            SECITEM_FreeItem(profile, PR_TRUE);
00486     }
00487 
00488     /* find cipher that is agreeable by all recipients and that has the most votes */
00489     max = 0;
00490     for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
00491        /* if not all of the recipients can do this, forget it */
00492        if (cipher_abilities[mapi] != rcount)
00493            continue;
00494        /* if cipher is not enabled or not allowed by policy, forget it */
00495        if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
00496            continue;
00497        /* if we're not doing fortezza, but the cipher is fortezza, forget it */
00498        if (!scert_is_fortezza  && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
00499            continue;
00500        /* now see if this one has more votes than the last best one */
00501        if (cipher_votes[mapi] >= max) {
00502            /* if equal number of votes, prefer the ones further down in the list */
00503            /* with the expectation that these are higher rated ciphers */
00504            chosen_cipher = smime_cipher_map[mapi].cipher;
00505            max = cipher_votes[mapi];
00506        }
00507     }
00508     /* if no common cipher was found, chosen_cipher stays at the default */
00509 
00510 done:
00511     if (poolp != NULL)
00512        PORT_FreeArena (poolp, PR_FALSE);
00513 
00514     return chosen_cipher;
00515 }
00516 
00517 /*
00518  * XXX This is a hack for now to satisfy our current interface.
00519  * Eventually, with more parameters needing to be specified, just
00520  * looking up the keysize is not going to be sufficient.
00521  */
00522 static int
00523 smime_keysize_by_cipher (unsigned long which)
00524 {
00525     int keysize;
00526 
00527     switch (which) {
00528       case SMIME_RC2_CBC_40:
00529        keysize = 40;
00530        break;
00531       case SMIME_RC2_CBC_64:
00532        keysize = 64;
00533        break;
00534       case SMIME_RC2_CBC_128:
00535        keysize = 128;
00536        break;
00537       case SMIME_DES_CBC_56:
00538       case SMIME_DES_EDE3_168:
00539       case SMIME_FORTEZZA:
00540        /*
00541         * These are special; since the key size is fixed, we actually
00542         * want to *avoid* specifying a key size.
00543         */
00544        keysize = 0;
00545        break;
00546       default:
00547        keysize = -1;
00548        break;
00549     }
00550 
00551     return keysize;
00552 }
00553 
00554 /*
00555  * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
00556  *
00557  * it would be great for UI purposes if there would be a way to find out which recipients
00558  * prevented a strong cipher from being used...
00559  */
00560 SECStatus
00561 NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
00562 {
00563     unsigned long cipher;
00564     int mapi;
00565 
00566     cipher = smime_choose_cipher(NULL, rcerts);
00567     mapi = smime_mapi_by_cipher(cipher);
00568 
00569     *bulkalgtag = smime_cipher_map[mapi].algtag;
00570     *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
00571 
00572     return SECSuccess;
00573 }
00574 
00575 /*
00576  * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
00577  *
00578  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
00579  * S/MIME capabilities attribute value.
00580  *
00581  * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
00582  * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
00583  *
00584  * "poolp" - arena pool to create the S/MIME capabilities data on
00585  * "dest" - SECItem to put the data in
00586  * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
00587  */
00588 SECStatus
00589 NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers)
00590 {
00591     NSSSMIMECapability *cap;
00592     NSSSMIMECapability **smime_capabilities;
00593     smime_cipher_map_entry *map;
00594     SECOidData *oiddata;
00595     SECItem *dummy;
00596     int i, capIndex;
00597 
00598     /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
00599     /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
00600     smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
00601                                   * sizeof(NSSSMIMECapability *));
00602     if (smime_capabilities == NULL)
00603        return SECFailure;
00604 
00605     capIndex = 0;
00606 
00607     /* Add all the symmetric ciphers
00608      * We walk the cipher list backwards, as it is ordered by increasing strength,
00609      * we prefer the stronger cipher over a weaker one, and we have to list the
00610      * preferred algorithm first */
00611     for (i = smime_cipher_map_count - 1; i >= 0; i--) {
00612        /* Find the corresponding entry in the cipher map. */
00613        map = &(smime_cipher_map[i]);
00614        if (!map->enabled)
00615            continue;
00616 
00617        /* If we're using a non-Fortezza cert, only advertise non-Fortezza
00618           capabilities. (We advertise all capabilities if we have a 
00619           Fortezza cert.) */
00620        if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA))
00621            continue;
00622 
00623        /* get next SMIME capability */
00624        cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
00625        if (cap == NULL)
00626            break;
00627        smime_capabilities[capIndex++] = cap;
00628 
00629        oiddata = SECOID_FindOIDByTag(map->algtag);
00630        if (oiddata == NULL)
00631            break;
00632 
00633        cap->capabilityID.data = oiddata->oid.data;
00634        cap->capabilityID.len = oiddata->oid.len;
00635        cap->parameters.data = map->parms ? map->parms->data : NULL;
00636        cap->parameters.len = map->parms ? map->parms->len : 0;
00637        cap->cipher = smime_cipher_map[i].cipher;
00638     }
00639 
00640     /* XXX add signature algorithms */
00641     /* XXX add key encipherment algorithms */
00642 
00643     smime_capabilities[capIndex] = NULL;  /* last one - now encode */
00644     dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
00645 
00646     /* now that we have the proper encoded SMIMECapabilities (or not),
00647      * free the work data */
00648     for (i = 0; smime_capabilities[i] != NULL; i++)
00649        PORT_Free(smime_capabilities[i]);
00650     PORT_Free(smime_capabilities);
00651 
00652     return (dummy == NULL) ? SECFailure : SECSuccess;
00653 }
00654 
00655 /*
00656  * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
00657  *
00658  * "poolp" - arena pool to create the attr value on
00659  * "dest" - SECItem to put the data in
00660  * "cert" - certificate that should be marked as preferred encryption key
00661  *          cert is expected to have been verified for EmailRecipient usage.
00662  */
00663 SECStatus
00664 NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
00665 {
00666     NSSSMIMEEncryptionKeyPreference ekp;
00667     SECItem *dummy = NULL;
00668     PLArenaPool *tmppoolp = NULL;
00669 
00670     if (cert == NULL)
00671        goto loser;
00672 
00673     tmppoolp = PORT_NewArena(1024);
00674     if (tmppoolp == NULL)
00675        goto loser;
00676 
00677     /* XXX hardcoded IssuerSN choice for now */
00678     ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
00679     ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
00680     if (ekp.id.issuerAndSN == NULL)
00681        goto loser;
00682 
00683     dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
00684 
00685 loser:
00686     if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
00687 
00688     return (dummy == NULL) ? SECFailure : SECSuccess;
00689 }
00690 
00691 /*
00692  * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
00693  *
00694  * "poolp" - arena pool to create the attr value on
00695  * "dest" - SECItem to put the data in
00696  * "cert" - certificate that should be marked as preferred encryption key
00697  *          cert is expected to have been verified for EmailRecipient usage.
00698  */
00699 SECStatus
00700 NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
00701 {
00702     SECItem *dummy = NULL;
00703     PLArenaPool *tmppoolp = NULL;
00704     CERTIssuerAndSN *isn;
00705 
00706     if (cert == NULL)
00707        goto loser;
00708 
00709     tmppoolp = PORT_NewArena(1024);
00710     if (tmppoolp == NULL)
00711        goto loser;
00712 
00713     isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
00714     if (isn == NULL)
00715        goto loser;
00716 
00717     dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
00718 
00719 loser:
00720     if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
00721 
00722     return (dummy == NULL) ? SECFailure : SECSuccess;
00723 }
00724 
00725 /*
00726  * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
00727  *                          find cert marked by EncryptionKeyPreference attribute
00728  *
00729  * "certdb" - handle for the cert database to look in
00730  * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
00731  *
00732  * if certificate is supposed to be found among the message's included certificates,
00733  * they are assumed to have been imported already.
00734  */
00735 CERTCertificate *
00736 NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
00737 {
00738     PLArenaPool *tmppoolp = NULL;
00739     CERTCertificate *cert = NULL;
00740     NSSSMIMEEncryptionKeyPreference ekp;
00741 
00742     tmppoolp = PORT_NewArena(1024);
00743     if (tmppoolp == NULL)
00744        return NULL;
00745 
00746     /* decode DERekp */
00747     if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
00748                                DERekp) != SECSuccess)
00749        goto loser;
00750 
00751     /* find cert */
00752     switch (ekp.selector) {
00753     case NSSSMIMEEncryptionKeyPref_IssuerSN:
00754        cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
00755        break;
00756     case NSSSMIMEEncryptionKeyPref_RKeyID:
00757     case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
00758        /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
00759        break;
00760     default:
00761        PORT_Assert(0);
00762     }
00763 loser:
00764     if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
00765 
00766     return cert;
00767 }
00768 
00769 extern const char __nss_smime_rcsid[];
00770 extern const char __nss_smime_sccsid[];
00771 
00772 PRBool
00773 NSSSMIME_VersionCheck(const char *importedVersion)
00774 {
00775     /*
00776      * This is the secret handshake algorithm.
00777      *
00778      * This release has a simple version compatibility
00779      * check algorithm.  This release is not backward
00780      * compatible with previous major releases.  It is
00781      * not compatible with future major, minor, or
00782      * patch releases.
00783      */
00784     volatile char c; /* force a reference that won't get optimized away */
00785 
00786     c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0]; 
00787 
00788     return NSS_VersionCheck(importedVersion);
00789 }
00790