tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

smimeutil.c (46412B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * Stuff specific to S/MIME policy and interoperability.
      7 */
      8 
      9 #include "secmime.h"
     10 #include "secoid.h"
     11 #include "pk11func.h"
     12 #include "ciferfam.h" /* for CIPHER_FAMILY symbols */
     13 #include "secasn1.h"
     14 #include "secitem.h"
     15 #include "sechash.h"
     16 #include "cert.h"
     17 #include "keyhi.h"
     18 #include "secerr.h"
     19 #include "cms.h"
     20 #include "nss.h"
     21 #include "prerror.h"
     22 #include "prinit.h"
     23 
     24 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
     25 SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
     26 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
     27 
     28 /*
     29 * XXX Would like the "parameters" field to be a SECItem *, but the
     30 * encoder is having trouble with optional pointers to an ANY.  Maybe
     31 * once that is fixed, can change this back...
     32 */
     33 typedef struct {
     34    SECItem capabilityID;
     35    SECItem parameters;
     36    long cipher; /* optimization */
     37 } NSSSMIMECapability;
     38 
     39 static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
     40    { SEC_ASN1_SEQUENCE,
     41      0, NULL, sizeof(NSSSMIMECapability) },
     42    { SEC_ASN1_OBJECT_ID,
     43      offsetof(NSSSMIMECapability, capabilityID) },
     44    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
     45      offsetof(NSSSMIMECapability, parameters) },
     46    { 0 }
     47 };
     48 
     49 static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
     50    { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
     51 };
     52 
     53 /*
     54 * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
     55 *  to store this and only this certificate permanently for the sender email address.
     56 */
     57 typedef enum {
     58    NSSSMIMEEncryptionKeyPref_IssuerSN,
     59    NSSSMIMEEncryptionKeyPref_RKeyID,
     60    NSSSMIMEEncryptionKeyPref_SubjectKeyID
     61 } NSSSMIMEEncryptionKeyPrefSelector;
     62 
     63 typedef struct {
     64    NSSSMIMEEncryptionKeyPrefSelector selector;
     65    union {
     66        CERTIssuerAndSN *issuerAndSN;
     67        NSSCMSRecipientKeyIdentifier *recipientKeyID;
     68        SECItem *subjectKeyID;
     69    } id;
     70 } NSSSMIMEEncryptionKeyPreference;
     71 
     72 extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
     73 
     74 static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
     75    { SEC_ASN1_CHOICE,
     76      offsetof(NSSSMIMEEncryptionKeyPreference, selector), NULL,
     77      sizeof(NSSSMIMEEncryptionKeyPreference) },
     78    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0 | SEC_ASN1_CONSTRUCTED,
     79      offsetof(NSSSMIMEEncryptionKeyPreference, id.issuerAndSN),
     80      SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
     81      NSSSMIMEEncryptionKeyPref_IssuerSN },
     82    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED,
     83      offsetof(NSSSMIMEEncryptionKeyPreference, id.recipientKeyID),
     84      NSSCMSRecipientKeyIdentifierTemplate,
     85      NSSSMIMEEncryptionKeyPref_RKeyID },
     86    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2 | SEC_ASN1_CONSTRUCTED,
     87      offsetof(NSSSMIMEEncryptionKeyPreference, id.subjectKeyID),
     88      SEC_ASN1_SUB(SEC_OctetStringTemplate),
     89      NSSSMIMEEncryptionKeyPref_SubjectKeyID },
     90    { 0 }
     91 };
     92 
     93 /* table of implemented key exchange algorithms. As we add algorithms,
     94 * update this table */
     95 static const SECOidTag implemented_key_encipherment[] = {
     96    SEC_OID_PKCS1_RSA_ENCRYPTION,
     97    SEC_OID_DHSINGLEPASS_STDDH_SHA1KDF_SCHEME,
     98    SEC_OID_DHSINGLEPASS_STDDH_SHA224KDF_SCHEME,
     99    SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME,
    100    SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME,
    101    SEC_OID_DHSINGLEPASS_STDDH_SHA512KDF_SCHEME,
    102    SEC_OID_DHSINGLEPASS_COFACTORDH_SHA1KDF_SCHEME,
    103    SEC_OID_DHSINGLEPASS_COFACTORDH_SHA224KDF_SCHEME,
    104    SEC_OID_DHSINGLEPASS_COFACTORDH_SHA256KDF_SCHEME,
    105    SEC_OID_DHSINGLEPASS_COFACTORDH_SHA384KDF_SCHEME,
    106    SEC_OID_DHSINGLEPASS_COFACTORDH_SHA512KDF_SCHEME,
    107 };
    108 static const int implemented_key_encipherment_len =
    109    PR_ARRAY_SIZE(implemented_key_encipherment);
    110 
    111 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
    112 typedef struct {
    113    unsigned long cipher;
    114    SECOidTag policytag;
    115 } smime_legacy_map_entry;
    116 
    117 /* legacy array of S/MIME values to map old SMIME entries to modern
    118 * algtags. */
    119 static const smime_legacy_map_entry smime_legacy_map[] = {
    120    /*    cipher, algtag, policy  */
    121    /*    ---------------------------------------    */
    122    { SMIME_RC2_CBC_40, SEC_OID_RC2_40_CBC },
    123    { SMIME_DES_CBC_56, SEC_OID_DES_CBC },
    124    { SMIME_RC2_CBC_64, SEC_OID_RC2_64_CBC },
    125    { SMIME_RC2_CBC_128, SEC_OID_RC2_128_CBC },
    126    { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC },
    127    { SMIME_AES_CBC_128, SEC_OID_AES_128_CBC },
    128    { SMIME_AES_CBC_256, SEC_OID_AES_256_CBC },
    129 };
    130 static const int smime_legacy_map_count = PR_ARRAY_SIZE(smime_legacy_map);
    131 
    132 static int
    133 smime_legacy_pref(SECOidTag algtag)
    134 {
    135    int i;
    136 
    137    for (i = 0; i < smime_legacy_map_count; i++) {
    138        if (smime_legacy_map[i].policytag == algtag)
    139            return i;
    140    }
    141    return -1;
    142 }
    143 
    144 /*
    145 * smime_legacy_to policy - find policy algtag from a legacy input
    146 */
    147 static SECOidTag
    148 smime_legacy_to_policy(unsigned long which)
    149 {
    150    int i;
    151 
    152    for (i = 0; i < smime_legacy_map_count; i++) {
    153        if (smime_legacy_map[i].cipher == which)
    154            return smime_legacy_map[i].policytag;
    155    }
    156    return SEC_OID_UNKNOWN;
    157 }
    158 
    159 /* map the old legacy values to modern oids. If the value isn't a recognized
    160 * legacy value, assume it's a SECOidTag and continue. This allows us to use
    161 * the old query and set interfaces with modern oids. */
    162 SECOidTag
    163 smime_legacy_to_oid(unsigned long which)
    164 {
    165    unsigned long mask;
    166 
    167    /* NOTE: all the legacy values and a CIPHER_FAMILYID of 0x00010000,
    168     * (CIPHER_FAMILYID_MASK is 0xffff0000). SECOidTags start at 0 and
    169     * increase monotonically, so as long as there is less than 16K of
    170     * tags, we can distinguish between values intended to be SMIME ciphers
    171     * and values intended to be SECOidTags */
    172    mask = which & CIPHER_FAMILYID_MASK;
    173    if (mask == CIPHER_FAMILYID_SMIME) {
    174        return smime_legacy_to_policy(which);
    175    }
    176    return (SECOidTag)which;
    177 }
    178 
    179 /* SEC_OID_RC2_CBC is actually 3 ciphers with different key lengths. All modern
    180 * symmetric ciphers include the key length with the oid. To handle policy for
    181 * the different keylengths, we include fake oids that let us map the policy based
    182 * on key length */
    183 static SECOidTag
    184 smime_get_policy_tag_from_key_length(SECOidTag algtag, unsigned long keybits)
    185 {
    186    if (algtag == SEC_OID_RC2_CBC) {
    187        switch (keybits) {
    188            case 40:
    189                return SEC_OID_RC2_40_CBC;
    190            case 64:
    191                return SEC_OID_RC2_64_CBC;
    192            case 128:
    193                return SEC_OID_RC2_128_CBC;
    194            default:
    195                break;
    196        }
    197        return SEC_OID_UNKNOWN;
    198    }
    199    return algtag;
    200 }
    201 
    202 PRBool
    203 smime_allowed_by_policy(SECOidTag algtag, PRUint32 neededPolicy)
    204 {
    205    PRUint32 policyFlags;
    206 
    207    /* some S/MIME algs map to the same underlying KEA mechanism,
    208     * collaps them here */
    209    if ((neededPolicy & (NSS_USE_ALG_IN_SMIME_KX | NSS_USE_ALG_IN_SMIME_KX_LEGACY)) != 0) {
    210        CK_MECHANISM_TYPE mechType = PK11_AlgtagToMechanism(algtag);
    211        switch (mechType) {
    212            case CKM_ECDH1_DERIVE:
    213            case CKM_ECDH1_COFACTOR_DERIVE:
    214                algtag = SEC_OID_ECDH_KEA;
    215                break;
    216        }
    217    }
    218 
    219    if ((NSS_GetAlgorithmPolicy(algtag, &policyFlags) == SECFailure) ||
    220        ((policyFlags & neededPolicy) != neededPolicy)) {
    221        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    222        return PR_FALSE;
    223    }
    224    return PR_TRUE;
    225 }
    226 
    227 /*
    228 * We'll need this for the fake policy oids for RC2, but the
    229 * rest of these should be moved to pk11wrap for generic
    230 * algtag to key size values. We already need this for
    231 * sec_pkcs5v2_key_length_by oid.
    232 */
    233 static int
    234 smime_keysize_by_cipher(SECOidTag algtag)
    235 {
    236    int keysize;
    237 
    238    switch (algtag) {
    239        case SEC_OID_RC2_40_CBC:
    240            keysize = 40;
    241            break;
    242        case SEC_OID_RC2_64_CBC:
    243            keysize = 64;
    244            break;
    245        case SEC_OID_RC2_128_CBC:
    246        case SEC_OID_AES_128_CBC:
    247        case SEC_OID_CAMELLIA_128_CBC:
    248            keysize = 128;
    249            break;
    250        case SEC_OID_AES_192_CBC:
    251        case SEC_OID_CAMELLIA_192_CBC:
    252            keysize = 192;
    253            break;
    254        case SEC_OID_AES_256_CBC:
    255        case SEC_OID_CAMELLIA_256_CBC:
    256            keysize = 256;
    257            break;
    258        default:
    259            keysize = 0;
    260            break;
    261    }
    262 
    263    return keysize;
    264 }
    265 
    266 static int
    267 smime_max_keysize_by_cipher(SECOidTag algtag)
    268 {
    269    int keysize = smime_keysize_by_cipher(algtag);
    270 
    271    if (keysize == 0) {
    272        CK_MECHANISM_TYPE mech = PK11_AlgtagToMechanism(algtag);
    273        return PK11_GetMaxKeyLength(mech) * PR_BITS_PER_BYTE;
    274    }
    275    return keysize;
    276 }
    277 
    278 SECOidTag
    279 smime_get_alg_from_policy(SECOidTag policy)
    280 {
    281    switch (policy) {
    282        case SEC_OID_RC2_40_CBC:
    283        case SEC_OID_RC2_64_CBC:
    284        case SEC_OID_RC2_128_CBC:
    285            return SEC_OID_RC2_CBC;
    286        default:
    287            break;
    288    }
    289    return policy;
    290 }
    291 
    292 typedef struct SMIMEListStr {
    293    SECOidTag *tags;
    294    size_t space_len;
    295    size_t array_len;
    296 } SMIMEList;
    297 
    298 static SMIMEList *smime_algorithm_list = NULL;
    299 static PZLock *algorithm_list_lock = NULL;
    300 static PRCallOnceType smime_init_arg = { 0 };
    301 
    302 /* return the number of algorithms in the list */
    303 size_t
    304 smime_list_length(const SMIMEList *list)
    305 {
    306    if ((list == NULL) || (list->tags == NULL)) {
    307        return 0;
    308    }
    309    return list->array_len;
    310 }
    311 
    312 /* find the index of the algtag in the list. If the algtag isn't on the list,
    313 * return the size of the list */
    314 size_t
    315 smime_list_index_find(const SMIMEList *list, SECOidTag algtag)
    316 {
    317    int i;
    318    if ((list == NULL) || (list->tags == NULL)) {
    319        return 0;
    320    }
    321    for (i = 0; i < list->array_len; i++) {
    322        if (algtag == list->tags[i]) {
    323            return i;
    324        }
    325    }
    326    return list->array_len;
    327 }
    328 
    329 #define SMIME_CHUNK_COUNT 10
    330 /* initialize and grow the list if necessary */
    331 static SECStatus
    332 smime_list_grow(SMIMEList **list)
    333 {
    334    /* first make sure the inital list is created */
    335    if (*list == NULL) {
    336        *list = PORT_ZNew(SMIMEList);
    337        if (*list == NULL) {
    338            return SECFailure;
    339        }
    340    }
    341    /* make sure the tag array is intialized */
    342    if ((*list)->tags == NULL) {
    343        (*list)->tags = PORT_ZNewArray(SECOidTag, SMIME_CHUNK_COUNT);
    344        if ((*list)->tags == NULL) {
    345            return SECFailure;
    346        }
    347        (*list)->space_len = SMIME_CHUNK_COUNT;
    348    }
    349    /* grow the tag array if necessary */
    350    if ((*list)->array_len == (*list)->space_len) {
    351        SECOidTag *new_space;
    352        size_t new_len = (*list)->space_len + SMIME_CHUNK_COUNT;
    353        new_space = (SECOidTag *)PORT_Realloc((*list)->tags,
    354                                              new_len * sizeof(SECOidTag));
    355        if (new_space) {
    356            return SECFailure;
    357        }
    358        (*list)->tags = new_space;
    359        (*list)->space_len = new_len;
    360    }
    361    return SECSuccess;
    362 }
    363 
    364 /* add a new algtag to the list. if the algtag is already on the list,
    365 * do nothing */
    366 static SECStatus
    367 smime_list_add(SMIMEList **list, SECOidTag algtag)
    368 {
    369    SECStatus rv;
    370    size_t array_len = smime_list_length(*list);
    371    size_t c_index = smime_list_index_find(*list, algtag);
    372 
    373    if (array_len != c_index) {
    374        /* already on the list */
    375        return SECSuccess;
    376    }
    377 
    378    /* go the list if necessary */
    379    rv = smime_list_grow(list);
    380    if (rv != SECSuccess) {
    381        return rv;
    382    }
    383    (*list)->tags[(*list)->array_len++] = algtag;
    384    return SECSuccess;
    385 }
    386 
    387 static SECStatus
    388 smime_list_remove(SMIMEList *list, SECOidTag algtag)
    389 {
    390    size_t c_index, i;
    391    size_t cipher_count = smime_list_length(list);
    392 
    393    if (cipher_count == 0) {
    394        return SECSuccess;
    395    }
    396    c_index = smime_list_index_find(list, algtag);
    397    if (c_index == cipher_count) {
    398        /* already removed from the list */
    399        return SECSuccess;
    400    }
    401    for (i = c_index; i < cipher_count - 1; i++) {
    402        list->tags[i] = list->tags[i + 1];
    403    }
    404    list->array_len--;
    405    list->tags[i] = 0;
    406    return SECSuccess;
    407 }
    408 
    409 static SECOidTag
    410 smime_list_fetch_by_index(const SMIMEList *list, size_t c_index)
    411 {
    412    size_t cipher_count = smime_list_length(list);
    413 
    414    if (c_index >= cipher_count) {
    415        return SEC_OID_UNKNOWN;
    416    }
    417    /* we know this is safe because list cipher_count is non-zero (if it were
    418     * any value of c_index will cause the above if to trigger */
    419    return list->tags[c_index];
    420 }
    421 
    422 static void
    423 smime_free_list(SMIMEList **list)
    424 {
    425    if (*list) {
    426        if ((*list)->tags) {
    427            PORT_Free((*list)->tags);
    428        }
    429        PORT_Free(*list);
    430    }
    431    *list = NULL;
    432 }
    433 
    434 static void
    435 smime_lock_algorithm_list(void)
    436 {
    437    PORT_Assert(algorithm_list_lock);
    438    if (algorithm_list_lock) {
    439        PZ_Lock(algorithm_list_lock);
    440    }
    441    return;
    442 }
    443 
    444 static void
    445 smime_unlock_algorithm_list(void)
    446 {
    447    PORT_Assert(algorithm_list_lock);
    448    if (algorithm_list_lock) {
    449        PZ_Unlock(algorithm_list_lock);
    450    }
    451    return;
    452 }
    453 
    454 static SECStatus
    455 smime_shutdown(void *appData, void *nssData)
    456 {
    457    if (algorithm_list_lock) {
    458        PZ_DestroyLock(algorithm_list_lock);
    459        algorithm_list_lock = NULL;
    460    }
    461    smime_free_list(&smime_algorithm_list);
    462    memset(&smime_init_arg, 0, sizeof(smime_init_arg));
    463    return SECSuccess;
    464 }
    465 
    466 static PRStatus
    467 smime_init_once(void *arg)
    468 {
    469    SECOidTag *tags = NULL;
    470    SECStatus rv;
    471    int tagCount;
    472    int i;
    473    int *error = (int *)arg;
    474    int *lengths = NULL;
    475    int *legacy_prefs = NULL;
    476 
    477    rv = NSS_RegisterShutdown(smime_shutdown, NULL);
    478    if (rv != SECSuccess) {
    479        *error = PORT_GetError();
    480        return PR_FAILURE;
    481    }
    482    algorithm_list_lock = PZ_NewLock(nssILockCache);
    483    if (algorithm_list_lock == NULL) {
    484        *error = PORT_GetError();
    485        return PR_FAILURE;
    486    }
    487 
    488    /* At initialization time, we need to set up the defaults. We first
    489     * look to see if the system or application has set up certain algorithms
    490     * by policy. If they have set up values by policy we'll only allow those
    491     * algorithms. We'll then look to see if any algorithms are enabled by
    492     * the application. */
    493    rv = NSS_GetAlgorithmPolicyAll(NSS_USE_ALG_IN_SMIME_LEGACY,
    494                                   NSS_USE_ALG_IN_SMIME_LEGACY,
    495                                   &tags, &tagCount);
    496    if (tags) {
    497        PORT_Free(tags);
    498        tags = NULL;
    499    }
    500    if ((rv != SECSuccess) || (tagCount == 0)) {
    501        /* No algorithms have been enabled by policy (either by the system
    502         * or by the application, we then will use the traditional default
    503         * algorithms from the policy map */
    504        for (i = smime_legacy_map_count - 1; i >= 0; i--) {
    505            SECOidTag policytag = smime_legacy_map[i].policytag;
    506            /* this enables the algorithm by policy. We need this or
    507             * the policy code will reject attempts to use it */
    508            NSS_SetAlgorithmPolicy(policytag, NSS_USE_ALG_IN_SMIME, 0);
    509            /* We also need to enable the algorithm. This is usually unde
    510             * application control once the defaults are set up, so the
    511             * application can turn off a policy that is already on, but
    512             * not turn on a policy that is already off */
    513            smime_list_add(&smime_algorithm_list, policytag);
    514        }
    515        return PR_SUCCESS;
    516    }
    517    /* We have a system supplied policy, do we also have
    518     * system supplied defaults? If we do we will only actually
    519     * turn on the algorithms that have been specified. */
    520    rv = NSS_GetAlgorithmPolicyAll(NSS_USE_DEFAULT_NOT_VALID |
    521                                       NSS_USE_DEFAULT_SMIME_ENABLE,
    522                                   NSS_USE_DEFAULT_SMIME_ENABLE,
    523                                   &tags, &tagCount);
    524    /* if none found, enable the default algorithms */
    525    if ((rv != SECSuccess) || (tagCount == 0)) {
    526        if (tags) {
    527            PORT_Free(tags);
    528            tags = NULL;
    529        }
    530        for (i = smime_legacy_map_count - 1; i >= 0; i--) {
    531            SECOidTag policytag = smime_legacy_map[i].policytag;
    532            /* we only enable the default algorithm, we don't change
    533             * it's policy, which the system has already set. NOTE:
    534             * what 'enable' means in the S/MIME sense is we advertise
    535             * that we can do the given algorithm in our smime capabilities. */
    536            smime_list_add(&smime_algorithm_list, policytag);
    537        }
    538        return PR_SUCCESS;
    539    }
    540 
    541    /* Sort tags by key strength here */
    542    lengths = PORT_ZNewArray(int, tagCount);
    543    if (lengths == NULL) {
    544        *error = PORT_GetError();
    545        goto loser;
    546    }
    547    legacy_prefs = PORT_ZNewArray(int, tagCount);
    548    if (lengths == NULL) {
    549        *error = PORT_GetError();
    550        goto loser;
    551    }
    552    /* Sort the tags array, highest preference at index 0 */
    553    for (i = 0; i < tagCount; i++) {
    554        int len = smime_max_keysize_by_cipher(tags[i]);
    555        int lpref = smime_legacy_pref(tags[i]);
    556        SECOidTag current = tags[i];
    557        PRBool shift = PR_FALSE;
    558        int j;
    559        /* Determine best position for tags[i].
    560         * For each position j, check if tags [i] has a higher preference.
    561         * If yes, store tags[i] at position j, and move all following
    562         * entries one position to the back of the array.
    563         */
    564        for (j = 0; j < i; j++) {
    565            int tlen = lengths[j];
    566            int tpref = legacy_prefs[j];
    567            SECOidTag ttag = tags[j];
    568            /* we prefer ciphers with bigger keysizes, then
    569             * we prefer ciphers in our historical list,
    570             * then we prefer ciphers that show up first
    571             * from the oid table */
    572            if (shift || (len > tlen) || ((len == tlen) && (lpref > tpref))) {
    573                tags[j] = current;
    574                lengths[j] = len;
    575                legacy_prefs[j] = lpref;
    576                current = ttag;
    577                len = tlen;
    578                lpref = tpref;
    579                shift = PR_TRUE;
    580            }
    581        }
    582        tags[i] = current;
    583        lengths[i] = len;
    584        legacy_prefs[i] = lpref;
    585    }
    586 
    587    /* put them in the enable list */
    588    for (i = 0; i < tagCount; i++) {
    589        smime_list_add(&smime_algorithm_list, tags[i]);
    590    }
    591    PORT_Free(lengths);
    592    PORT_Free(legacy_prefs);
    593    PORT_Free(tags);
    594    return PR_SUCCESS;
    595 loser:
    596    if (lengths)
    597        PORT_Free(lengths);
    598    if (legacy_prefs)
    599        PORT_Free(legacy_prefs);
    600    if (tags)
    601        PORT_Free(tags);
    602    return PR_FAILURE;
    603 }
    604 
    605 static SECStatus
    606 smime_init(void)
    607 {
    608    static PRBool smime_policy_initted = PR_FALSE;
    609    static int error = 0;
    610    PRStatus nrv;
    611 
    612    /* has NSS been initialized? */
    613    if (!NSS_IsInitialized()) {
    614        PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
    615        return SECFailure;
    616    }
    617    if (smime_policy_initted) {
    618        return SECSuccess;
    619    }
    620    nrv = PR_CallOnceWithArg(&smime_init_arg, smime_init_once, &error);
    621    if (nrv == PR_SUCCESS) {
    622        smime_policy_initted = PR_TRUE;
    623        return SECSuccess;
    624    }
    625    PORT_SetError(error);
    626    return SECFailure;
    627 }
    628 
    629 /*
    630 * NSS_SMIME_EnableCipher - this function locally records the user's preference
    631 */
    632 SECStatus
    633 NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
    634 {
    635    SECOidTag algtag;
    636 
    637    SECStatus rv = smime_init();
    638    if (rv != SECSuccess) {
    639        return SECFailure;
    640    }
    641 
    642    algtag = smime_legacy_to_oid(which);
    643    if (!smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME)) {
    644        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    645        return SECFailure;
    646    }
    647 
    648    smime_lock_algorithm_list();
    649    if (on) {
    650        rv = smime_list_add(&smime_algorithm_list, algtag);
    651    } else {
    652        rv = smime_list_remove(smime_algorithm_list, algtag);
    653    }
    654    smime_unlock_algorithm_list();
    655    return rv;
    656 }
    657 
    658 /*
    659 * this function locally records the export policy
    660 */
    661 SECStatus
    662 NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
    663 {
    664    SECOidTag algtag = smime_legacy_to_oid(which);
    665    PRUint32 set = on ? NSS_USE_ALG_IN_SMIME : 0;
    666    PRUint32 clear = on ? 0 : NSS_USE_ALG_IN_SMIME;
    667    /* make sure we are inited before setting, so
    668     * the defaults are correct */
    669    SECStatus rv = smime_init();
    670    if (rv != SECSuccess) {
    671        return SECFailure;
    672    }
    673 
    674    return NSS_SetAlgorithmPolicy(algtag, set, clear);
    675 }
    676 
    677 PRBool
    678 NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
    679 {
    680    SECOidTag algtag;
    681    /* make sure we are inited before checking policy, so
    682     * the defaults are correct */
    683    SECStatus rv = smime_init();
    684    if (rv != SECSuccess) {
    685        return SECFailure;
    686    }
    687 
    688    algtag = smime_get_policy_tag_from_key_length(SECOID_GetAlgorithmTag(algid),
    689                                                  PK11_GetKeyStrength(key, algid));
    690    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_LEGACY);
    691 }
    692 
    693 PRBool
    694 NSS_SMIMEUtil_EncryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
    695 {
    696    SECOidTag algtag;
    697    /* make sure we are inited before checking policy, so
    698     * the defaults are correct */
    699    SECStatus rv = smime_init();
    700    if (rv != SECSuccess) {
    701        return SECFailure;
    702    }
    703 
    704    algtag = smime_get_policy_tag_from_key_length(SECOID_GetAlgorithmTag(algid),
    705                                                  PK11_GetKeyStrength(key, algid));
    706    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME);
    707 }
    708 
    709 PRBool
    710 NSS_SMIMEUtil_SigningAllowed(SECAlgorithmID *algid)
    711 {
    712    SECOidTag algtag;
    713    /* we don't adjust SIGNATURE policy based on defaults, so no need
    714     * to call smime_init() */
    715 
    716    algtag = SECOID_GetAlgorithmTag(algid);
    717    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_SIGNATURE);
    718 }
    719 
    720 static PRBool
    721 nss_smime_enforce_key_size(void)
    722 {
    723    PRInt32 optFlags;
    724 
    725    if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) {
    726        if (optFlags & NSS_KEY_SIZE_POLICY_SMIME_FLAG) {
    727            return PR_TRUE;
    728        }
    729    }
    730    return PR_FALSE;
    731 }
    732 
    733 PRBool
    734 NSS_SMIMEUtil_KeyEncodingAllowed(SECAlgorithmID *algid, CERTCertificate *cert,
    735                                 SECKEYPublicKey *key)
    736 {
    737    SECOidTag algtag;
    738    /* we don't adjust KEA policy based on defaults, so no need
    739     * to call smime_init() */
    740 
    741    /* if required, make sure the key lengths are enforced */
    742    if (nss_smime_enforce_key_size()) {
    743        SECStatus rv;
    744        PRBool freeKey = PR_FALSE;
    745 
    746        if (!key) {
    747            /* either the public key or the cert must be supplied. If the
    748             * key wasn't supplied, get it from the certificate */
    749            if (!cert) {
    750                PORT_SetError(SEC_ERROR_INVALID_ARGS);
    751                return PR_FALSE;
    752            }
    753            key = CERT_ExtractPublicKey(cert);
    754            freeKey = PR_TRUE;
    755        }
    756        rv = SECKEY_EnforceKeySize(key->keyType,
    757                                   SECKEY_PublicKeyStrengthInBits(key),
    758                                   SEC_ERROR_BAD_EXPORT_ALGORITHM);
    759        if (freeKey) {
    760            SECKEY_DestroyPublicKey(key);
    761        }
    762        if (rv != SECSuccess) {
    763            return PR_FALSE;
    764        }
    765    }
    766    algtag = SECOID_GetAlgorithmTag(algid);
    767    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_KX);
    768 }
    769 
    770 PRBool
    771 NSS_SMIMEUtil_KeyDecodingAllowed(SECAlgorithmID *algid, SECKEYPrivateKey *key)
    772 {
    773    SECOidTag algtag;
    774    /* we don't adjust KEA policy based on defaults, so no need
    775     * to call smime_init() */
    776 
    777    /* if required, make sure the key lengths are enforced */
    778    if (nss_smime_enforce_key_size()) {
    779        SECStatus rv;
    780        rv = SECKEY_EnforceKeySize(key->keyType,
    781                                   SECKEY_PrivateKeyStrengthInBits(key),
    782                                   SEC_ERROR_BAD_EXPORT_ALGORITHM);
    783        if (rv != SECSuccess) {
    784            return PR_FALSE;
    785        }
    786    }
    787    algtag = SECOID_GetAlgorithmTag(algid);
    788    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_KX_LEGACY);
    789 }
    790 
    791 /*
    792 * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
    793 *
    794 * This tells whether or not *any* S/MIME encryption can be done,
    795 * according to policy.  Callers may use this to do nicer user interface
    796 * (say, greying out a checkbox so a user does not even try to encrypt
    797 * a message when they are not allowed to) or for any reason they want
    798 * to check whether S/MIME encryption (or decryption, for that matter)
    799 * may be done.
    800 *
    801 * It takes no arguments.  The return value is a simple boolean:
    802 *   PR_TRUE means encryption (or decryption) is *possible*
    803 *      (but may still fail due to other reasons, like because we cannot
    804 *      find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
    805 *   PR_FALSE means encryption (or decryption) is not permitted
    806 *
    807 * There are no errors from this routine.
    808 */
    809 PRBool
    810 NSS_SMIMEUtil_EncryptionPossible(void)
    811 {
    812    SECStatus rv = smime_init();
    813    size_t len;
    814    if (rv != SECSuccess) {
    815        return SECFailure;
    816    }
    817    smime_lock_algorithm_list();
    818    len = smime_list_length(smime_algorithm_list);
    819    smime_unlock_algorithm_list();
    820    return len != 0 ? PR_TRUE : PR_FALSE;
    821 }
    822 
    823 PRBool
    824 NSS_SMIMEUtil_EncryptionEnabled(int which)
    825 {
    826    SECOidTag algtag;
    827    size_t c_index, len;
    828 
    829    SECStatus rv = smime_init();
    830    if (rv != SECSuccess) {
    831        return SECFailure;
    832    }
    833 
    834    algtag = smime_legacy_to_oid(which);
    835 
    836    smime_lock_algorithm_list();
    837    len = smime_list_length(smime_algorithm_list);
    838    c_index = smime_list_index_find(smime_algorithm_list, algtag);
    839    smime_unlock_algorithm_list();
    840 
    841    if (len >= c_index) {
    842        return PR_FALSE;
    843    }
    844 
    845    return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME);
    846 }
    847 
    848 static SECOidTag
    849 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
    850 {
    851    SECOidTag capIDTag;
    852 
    853    /* we need the OIDTag here */
    854    capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
    855 
    856    /* RC2 used a generic oid and encoded the key length in the
    857     * parameters */
    858    if (capIDTag == SEC_OID_RC2_CBC) {
    859        SECStatus rv;
    860        unsigned long key_bits;
    861        SECItem keyItem = { siBuffer, NULL, 0 };
    862 
    863        rv = SEC_ASN1DecodeItem(NULL, &keyItem,
    864                                SEC_ASN1_GET(SEC_IntegerTemplate), &cap->parameters);
    865        if (rv != SECSuccess) {
    866            return SEC_OID_UNKNOWN;
    867        }
    868        rv = SEC_ASN1DecodeInteger(&keyItem, &key_bits);
    869        SECITEM_FreeItem(&keyItem, PR_FALSE);
    870        if (rv != SECSuccess) {
    871            return SEC_OID_UNKNOWN;
    872        }
    873        return smime_get_policy_tag_from_key_length(capIDTag, key_bits);
    874    }
    875 
    876    /* everything else uses a null parameter */
    877    if (!cap->parameters.data || !cap->parameters.len) {
    878        return capIDTag;
    879    }
    880    if (cap->parameters.len == 2 &&
    881        cap->parameters.data[0] == SEC_ASN1_NULL &&
    882        cap->parameters.data[1] == 0) {
    883        return capIDTag;
    884    }
    885    return SEC_OID_UNKNOWN;
    886 }
    887 
    888 /*
    889 * smime_choose_cipher - choose a cipher that works for all the recipients
    890 *
    891 * "rcerts" - recipient's certificates
    892 */
    893 static SECOidTag
    894 smime_choose_cipher(CERTCertificate **rcerts)
    895 {
    896    PLArenaPool *poolp = NULL;
    897    SECOidTag chosen_cipher = SEC_OID_UNKNOWN;
    898    size_t cipher_count;
    899    SECOidTag cipher;
    900    int *cipher_abilities;
    901    int *cipher_votes;
    902    size_t weak_index;
    903    size_t strong_index;
    904    size_t aes128_index;
    905    size_t aes256_index;
    906    size_t c_index;
    907    int rcount, max;
    908 
    909    smime_lock_algorithm_list();
    910    cipher_count = smime_list_length(smime_algorithm_list);
    911    if (cipher_count == 0) {
    912        goto done;
    913    }
    914 
    915    chosen_cipher = SEC_OID_RC2_40_CBC; /* the default, LCD */
    916    weak_index = smime_list_index_find(smime_algorithm_list, chosen_cipher);
    917    strong_index = smime_list_index_find(smime_algorithm_list, SEC_OID_DES_EDE3_CBC);
    918    aes128_index = smime_list_index_find(smime_algorithm_list, SEC_OID_AES_128_CBC);
    919    aes256_index = smime_list_index_find(smime_algorithm_list, SEC_OID_AES_256_CBC);
    920    /* make sure the default selected cipher is enabled */
    921    if (weak_index == cipher_count) {
    922        chosen_cipher = SEC_OID_DES_EDE3_CBC;
    923        if (strong_index == cipher_count) {
    924            chosen_cipher = SEC_OID_AES_128_CBC;
    925            if (aes128_index == cipher_count) {
    926                chosen_cipher = SEC_OID_AES_256_CBC;
    927                if (aes256_index == cipher_count) {
    928                    /* none of the standard algorithms are enabled, If the
    929                     * recipients don't explicitly include a better cipher
    930                     * then fail */
    931                    chosen_cipher = SEC_OID_UNKNOWN;
    932                }
    933            }
    934        }
    935    }
    936 
    937    poolp = PORT_NewArena(1024); /* XXX what is right value? */
    938    if (poolp == NULL)
    939        goto done;
    940 
    941    cipher_abilities = PORT_ArenaZNewArray(poolp, int, cipher_count + 1);
    942    cipher_votes = PORT_ArenaZNewArray(poolp, int, cipher_count + 1);
    943    if (cipher_votes == NULL || cipher_abilities == NULL) {
    944        goto done;
    945    }
    946 
    947    /* Make triple-DES the strong cipher. */
    948 
    949    /* walk all the recipient's certs */
    950    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
    951        SECItem *profile;
    952        NSSSMIMECapability **caps;
    953        int pref;
    954 
    955        /* the first cipher that matches in the user's SMIME profile gets
    956         * "cipher_count" votes; the next one gets "cipher_count" - 1
    957         * and so on. If every cipher matches, the last one gets 1 (one) vote */
    958        pref = cipher_count;
    959 
    960        /* find recipient's SMIME profile */
    961        profile = CERT_FindSMimeProfile(rcerts[rcount]);
    962 
    963        if (profile != NULL && profile->data != NULL && profile->len > 0) {
    964            /* we have a profile (still DER-encoded) */
    965            caps = NULL;
    966            /* decode it */
    967            if (SEC_QuickDERDecodeItem(poolp, &caps,
    968                                       NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
    969                caps != NULL) {
    970                int i;
    971                /* walk the SMIME capabilities for this recipient */
    972                for (i = 0; caps[i] != NULL; i++) {
    973                    cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
    974                    c_index = smime_list_index_find(smime_algorithm_list, cipher);
    975                    if (c_index < cipher_count) {
    976                        /* found the cipher */
    977                        cipher_abilities[c_index]++;
    978                        cipher_votes[c_index] += pref;
    979                        --pref;
    980                    }
    981                }
    982            }
    983        } else {
    984            /* no profile found - so we can only assume that the user can do
    985             * the mandatory algorithms which are RC2-40 (weak crypto) and
    986             * 3DES (strong crypto), unless the user has an elliptic curve
    987             * key.  For elliptic curve keys, RFC 5753 mandates support
    988             * for AES 128 CBC. */
    989            SECKEYPublicKey *key;
    990            unsigned int pklen_bits;
    991            KeyType key_type;
    992 
    993            /*
    994             * if recipient's public key length is > 512, vote for a strong cipher
    995             * please not that the side effect of this is that if only one recipient
    996             * has an export-level public key, the strong cipher is disabled.
    997             *
    998             * XXX This is probably only good for RSA keys.  What I would
    999             * really like is a function to just say;  Is the public key in
   1000             * this cert an export-length key?  Then I would not have to
   1001             * know things like the value 512, or the kind of key, or what
   1002             * a subjectPublicKeyInfo is, etc.
   1003             */
   1004            key = CERT_ExtractPublicKey(rcerts[rcount]);
   1005            pklen_bits = 0;
   1006            key_type = nullKey;
   1007            if (key != NULL) {
   1008                pklen_bits = SECKEY_PublicKeyStrengthInBits(key);
   1009                key_type = SECKEY_GetPublicKeyType(key);
   1010                SECKEY_DestroyPublicKey(key);
   1011                key = NULL;
   1012            }
   1013 
   1014            if (key_type == ecKey) {
   1015                /* While RFC 5753 mandates support for AES-128 CBC, should use
   1016                 * AES 256 if user's key provides more than 128 bits of
   1017                 * security strength so that symmetric key is not weak link. */
   1018 
   1019                /* RC2-40 is not compatible with elliptic curve keys. */
   1020                if (chosen_cipher == SEC_OID_RC2_40_CBC) {
   1021                    chosen_cipher = SEC_OID_AES_128_CBC;
   1022                }
   1023                if (pklen_bits > 256) {
   1024                    cipher_abilities[aes256_index]++;
   1025                    cipher_votes[aes256_index] += pref;
   1026                    pref--;
   1027                }
   1028                cipher_abilities[aes128_index]++;
   1029                cipher_votes[aes128_index] += pref;
   1030                pref--;
   1031                cipher_abilities[strong_index]++;
   1032                cipher_votes[strong_index] += pref;
   1033                pref--;
   1034            } else {
   1035                if (pklen_bits > 3072) {
   1036                    /* While support for AES 256 is a SHOULD+ in RFC 5751
   1037                     * rather than a MUST, RSA and DSA keys longer than 3072
   1038                     * bits provide more than 128 bits of security strength.
   1039                     * So, AES 256 should be used to provide comparable
   1040                     * security. */
   1041                    cipher_abilities[aes256_index]++;
   1042                    cipher_votes[aes256_index] += pref;
   1043                    pref--;
   1044                }
   1045                if (pklen_bits > 1023) {
   1046                    /* RFC 5751 mandates support for AES 128, but also says
   1047                     * that RSA and DSA signature keys SHOULD NOT be less than
   1048                     * 1024 bits. So, cast vote for AES 128 if key length
   1049                     * is at least 1024 bits. */
   1050                    cipher_abilities[aes128_index]++;
   1051                    cipher_votes[aes128_index] += pref;
   1052                    pref--;
   1053                }
   1054                if (pklen_bits > 512) {
   1055                    /* cast votes for the strong algorithm */
   1056                    cipher_abilities[strong_index]++;
   1057                    cipher_votes[strong_index] += pref;
   1058                    pref--;
   1059                }
   1060 
   1061                /* always cast (possibly less) votes for the weak algorithm */
   1062                cipher_abilities[weak_index]++;
   1063                cipher_votes[weak_index] += pref;
   1064            }
   1065        }
   1066        if (profile != NULL)
   1067            SECITEM_FreeItem(profile, PR_TRUE);
   1068    }
   1069 
   1070    /* find cipher that is agreeable by all recipients and that has the most votes */
   1071    max = 0;
   1072    for (c_index = 0; c_index < cipher_count; c_index++) {
   1073        /* if not all of the recipients can do this, forget it */
   1074        if (cipher_abilities[c_index] != rcount)
   1075            continue;
   1076        cipher = smime_list_fetch_by_index(smime_algorithm_list, c_index);
   1077        /* if cipher is allowed by policy, forget it */
   1078        if (!smime_allowed_by_policy(cipher, NSS_USE_ALG_IN_SMIME)) {
   1079            continue;
   1080        }
   1081        /* now see if this one has more votes than the last best one */
   1082        if (cipher_votes[c_index] >= max) {
   1083            /* if equal number of votes, prefer the ones further down in the list */
   1084            /* with the expectation that these are higher rated ciphers */
   1085            chosen_cipher = cipher;
   1086            max = cipher_votes[c_index];
   1087        }
   1088    }
   1089    /* if no common cipher was found, chosen_cipher stays at the default */
   1090 
   1091 done:
   1092    smime_unlock_algorithm_list();
   1093    if (poolp != NULL)
   1094        PORT_FreeArena(poolp, PR_FALSE);
   1095 
   1096    return chosen_cipher;
   1097 }
   1098 
   1099 /*
   1100 * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
   1101 *
   1102 * it would be great for UI purposes if there would be a way to find out which recipients
   1103 * prevented a strong cipher from being used...
   1104 */
   1105 SECStatus
   1106 NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
   1107                                       SECOidTag *bulkalgtag, int *keysize)
   1108 {
   1109    SECOidTag cipher;
   1110 
   1111    SECStatus rv = smime_init();
   1112    if (rv != SECSuccess) {
   1113        return SECFailure;
   1114    }
   1115 
   1116    cipher = smime_choose_cipher(rcerts);
   1117    if (cipher == SEC_OID_UNKNOWN) {
   1118        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
   1119        return SECFailure;
   1120    }
   1121 
   1122    *bulkalgtag = smime_get_alg_from_policy(cipher);
   1123    *keysize = smime_keysize_by_cipher(cipher);
   1124 
   1125    return SECSuccess;
   1126 }
   1127 
   1128 /*
   1129 * Create a new Capability from an oid tag
   1130 */
   1131 static NSSSMIMECapability *
   1132 smime_create_capability(SECOidTag cipher)
   1133 {
   1134    NSSSMIMECapability *cap = NULL;
   1135    SECOidData *oiddata = NULL;
   1136    SECItem *dummy = NULL;
   1137 
   1138    oiddata = SECOID_FindOIDByTag(smime_get_alg_from_policy(cipher));
   1139    if (oiddata == NULL) {
   1140        return NULL;
   1141    }
   1142 
   1143    cap = PORT_ZNew(NSSSMIMECapability);
   1144    if (cap == NULL) {
   1145        return NULL;
   1146    }
   1147 
   1148    cap->capabilityID.data = oiddata->oid.data;
   1149    cap->capabilityID.len = oiddata->oid.len;
   1150    if (cipher == SEC_OID_RC2_CBC) {
   1151        SECItem keyItem = { siBuffer, NULL, 0 };
   1152        unsigned long keybits = smime_get_alg_from_policy(cipher);
   1153        dummy = SEC_ASN1EncodeInteger(NULL, &keyItem, keybits);
   1154        if (dummy == NULL) {
   1155            PORT_Free(cap);
   1156            return NULL;
   1157        }
   1158        dummy = SEC_ASN1EncodeItem(NULL, &cap->parameters,
   1159                                   &keyItem, SEC_ASN1_GET(SEC_IntegerTemplate));
   1160        SECITEM_FreeItem(&keyItem, PR_FALSE);
   1161        if (dummy == NULL) {
   1162            PORT_Free(cap);
   1163            return NULL;
   1164        }
   1165    } else {
   1166        cap->parameters.data = NULL;
   1167        cap->parameters.len = 0;
   1168    }
   1169    return cap;
   1170 }
   1171 /*
   1172 * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
   1173 *
   1174 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
   1175 * S/MIME capabilities attribute value.
   1176 *
   1177 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
   1178 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
   1179 *
   1180 * "poolp" - arena pool to create the S/MIME capabilities data on
   1181 * "dest" - SECItem to put the data in
   1182 */
   1183 SECStatus
   1184 NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
   1185 {
   1186    NSSSMIMECapability *cap = NULL;
   1187    NSSSMIMECapability **smime_capabilities = NULL;
   1188    SECItem *dummy = NULL;
   1189    int i, capIndex;
   1190    int cap_count;
   1191    int cipher_count;
   1192    int hash_count;
   1193 
   1194    SECStatus rv = smime_init();
   1195    if (rv != SECSuccess) {
   1196        return SECFailure;
   1197    }
   1198    /* First get the hash count */
   1199    for (i = HASH_AlgNULL + 1;; i++) {
   1200        if (HASH_GetHashOidTagByHashType(i) == SEC_OID_UNKNOWN) {
   1201            break;
   1202        }
   1203    }
   1204    hash_count = i - 1;
   1205 
   1206    smime_lock_algorithm_list();
   1207    /* now get the cipher count */
   1208    cipher_count = smime_list_length(smime_algorithm_list);
   1209    if (cipher_count == 0) {
   1210        smime_unlock_algorithm_list();
   1211        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1212        return SECFailure;
   1213    }
   1214 
   1215    cap_count = cipher_count + hash_count + implemented_key_encipherment_len;
   1216 
   1217    /* cipher_count + 1 is an upper bound - we might end up with less */
   1218    smime_capabilities = PORT_ZNewArray(NSSSMIMECapability *, cap_count + 1);
   1219    if (smime_capabilities == NULL) {
   1220        smime_unlock_algorithm_list();
   1221        return SECFailure;
   1222    }
   1223 
   1224    capIndex = 0;
   1225 
   1226    /* Add all the symmetric ciphers
   1227     * We walk the cipher list,  as it is ordered by decreasing strength,
   1228     * we prefer the stronger cipher over a weaker one, and we have to list the
   1229     * preferred algorithm first */
   1230    for (i = 0; i < cipher_count; i++) {
   1231        SECOidTag cipher = smime_list_fetch_by_index(smime_algorithm_list, i);
   1232 
   1233        /* is it allowed by policy? */
   1234        if (!smime_allowed_by_policy(cipher, NSS_USE_ALG_IN_SMIME)) {
   1235            continue;
   1236        }
   1237        cipher = smime_get_alg_from_policy(cipher);
   1238 
   1239        cap = smime_create_capability(cipher);
   1240        if (cap == NULL)
   1241            break;
   1242        smime_capabilities[capIndex++] = cap;
   1243    }
   1244    /* add signature algorithms = hash algs.
   1245     * probably also need to figure how what
   1246     * actual signatures we support in secvfy
   1247     * as well. We currently don't look a these
   1248     * when choosing hash and signature (hash is
   1249     * chosen by the application and signature
   1250     * type is chosen by the signing cert/key) */
   1251    smime_unlock_algorithm_list();
   1252    for (i = HASH_AlgNULL + 1; i < hash_count + 1; i++) {
   1253        SECOidTag hash_alg = HASH_GetHashOidTagByHashType(i);
   1254 
   1255        if (!smime_allowed_by_policy(hash_alg,
   1256                                     NSS_USE_ALG_IN_SMIME_SIGNATURE | NSS_USE_ALG_IN_SIGNATURE)) {
   1257            continue;
   1258        }
   1259        cap = smime_create_capability(hash_alg);
   1260        /* get next SMIME capability */
   1261        if (cap == NULL)
   1262            break;
   1263        smime_capabilities[capIndex++] = cap;
   1264    }
   1265 
   1266    /* add key encipherment algorithms . These are static
   1267     * to the s/mime library, so we can just use the table.
   1268     * new kea algs should be implemented. We don't use these
   1269     * because the senders key pretty much selects what time
   1270     * of kea we are going to implement */
   1271    for (i = 0; i < implemented_key_encipherment_len; i++) {
   1272        SECOidTag kea_alg = implemented_key_encipherment[i];
   1273 
   1274        if (!smime_allowed_by_policy(kea_alg, NSS_USE_ALG_IN_SMIME_KX)) {
   1275            continue;
   1276        }
   1277        cap = smime_create_capability(kea_alg);
   1278        /* get next SMIME capability */
   1279        if (cap == NULL)
   1280            break;
   1281        smime_capabilities[capIndex++] = cap;
   1282    }
   1283 
   1284    smime_capabilities[capIndex] = NULL; /* last one - now encode */
   1285    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
   1286 
   1287    /* now that we have the proper encoded SMIMECapabilities (or not),
   1288     * free the work data */
   1289    for (i = 0; smime_capabilities[i] != NULL; i++) {
   1290        if (smime_capabilities[i]->parameters.data) {
   1291            PORT_Free(smime_capabilities[i]->parameters.data);
   1292        }
   1293        PORT_Free(smime_capabilities[i]);
   1294    }
   1295    PORT_Free(smime_capabilities);
   1296 
   1297    return (dummy == NULL) ? SECFailure : SECSuccess;
   1298 }
   1299 
   1300 /*
   1301 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
   1302 *
   1303 * "poolp" - arena pool to create the attr value on
   1304 * "dest" - SECItem to put the data in
   1305 * "cert" - certificate that should be marked as preferred encryption key
   1306 *          cert is expected to have been verified for EmailRecipient usage.
   1307 */
   1308 SECStatus
   1309 NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
   1310 {
   1311    NSSSMIMEEncryptionKeyPreference ekp;
   1312    SECItem *dummy = NULL;
   1313    PLArenaPool *tmppoolp = NULL;
   1314 
   1315    if (cert == NULL)
   1316        goto loser;
   1317 
   1318    tmppoolp = PORT_NewArena(1024);
   1319    if (tmppoolp == NULL)
   1320        goto loser;
   1321 
   1322    /* XXX hardcoded IssuerSN choice for now */
   1323    ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
   1324    ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
   1325    if (ekp.id.issuerAndSN == NULL)
   1326        goto loser;
   1327 
   1328    dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
   1329 
   1330 loser:
   1331    if (tmppoolp)
   1332        PORT_FreeArena(tmppoolp, PR_FALSE);
   1333 
   1334    return (dummy == NULL) ? SECFailure : SECSuccess;
   1335 }
   1336 
   1337 /*
   1338 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
   1339 *
   1340 * "poolp" - arena pool to create the attr value on
   1341 * "dest" - SECItem to put the data in
   1342 * "cert" - certificate that should be marked as preferred encryption key
   1343 *          cert is expected to have been verified for EmailRecipient usage.
   1344 */
   1345 SECStatus
   1346 NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
   1347 {
   1348    SECItem *dummy = NULL;
   1349    PLArenaPool *tmppoolp = NULL;
   1350    CERTIssuerAndSN *isn;
   1351 
   1352    if (cert == NULL)
   1353        goto loser;
   1354 
   1355    tmppoolp = PORT_NewArena(1024);
   1356    if (tmppoolp == NULL)
   1357        goto loser;
   1358 
   1359    isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
   1360    if (isn == NULL)
   1361        goto loser;
   1362 
   1363    dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
   1364 
   1365 loser:
   1366    if (tmppoolp)
   1367        PORT_FreeArena(tmppoolp, PR_FALSE);
   1368 
   1369    return (dummy == NULL) ? SECFailure : SECSuccess;
   1370 }
   1371 
   1372 /*
   1373 * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
   1374 *                              find cert marked by EncryptionKeyPreference attribute
   1375 *
   1376 * "certdb" - handle for the cert database to look in
   1377 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
   1378 *
   1379 * if certificate is supposed to be found among the message's included certificates,
   1380 * they are assumed to have been imported already.
   1381 */
   1382 CERTCertificate *
   1383 NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
   1384 {
   1385    PLArenaPool *tmppoolp = NULL;
   1386    CERTCertificate *cert = NULL;
   1387    NSSSMIMEEncryptionKeyPreference ekp;
   1388 
   1389    tmppoolp = PORT_NewArena(1024);
   1390    if (tmppoolp == NULL)
   1391        return NULL;
   1392 
   1393    /* decode DERekp */
   1394    if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
   1395                               DERekp) != SECSuccess)
   1396        goto loser;
   1397 
   1398    /* find cert */
   1399    switch (ekp.selector) {
   1400        case NSSSMIMEEncryptionKeyPref_IssuerSN:
   1401            cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
   1402            break;
   1403        case NSSSMIMEEncryptionKeyPref_RKeyID:
   1404        case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
   1405            /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
   1406            break;
   1407        default:
   1408            PORT_Assert(0);
   1409    }
   1410 loser:
   1411    if (tmppoolp)
   1412        PORT_FreeArena(tmppoolp, PR_FALSE);
   1413 
   1414    return cert;
   1415 }
   1416 
   1417 extern const char __nss_smime_version[];
   1418 
   1419 PRBool
   1420 NSSSMIME_VersionCheck(const char *importedVersion)
   1421 {
   1422 #define NSS_VERSION_VARIABLE __nss_smime_version
   1423 #include "verref.h"
   1424    /*
   1425     * This is the secret handshake algorithm.
   1426     *
   1427     * This release has a simple version compatibility
   1428     * check algorithm.  This release is not backward
   1429     * compatible with previous major releases.  It is
   1430     * not compatible with future major, minor, or
   1431     * patch releases.
   1432     */
   1433    return NSS_VersionCheck(importedVersion);
   1434 }
   1435 
   1436 const char *
   1437 NSSSMIME_GetVersion(void)
   1438 {
   1439    return NSS_VERSION;
   1440 }