Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/smime/smimeutil.c
Line
Count
Source (jump to first uncovered line)
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 "cert.h"
16
#include "keyhi.h"
17
#include "secerr.h"
18
#include "cms.h"
19
#include "nss.h"
20
21
SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
22
SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
23
SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
24
25
/* various integer's ASN.1 encoding */
26
static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
27
static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
28
static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
29
30
/* RC2 algorithm parameters (used in smime_cipher_map) */
31
static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
32
static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
33
static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
34
35
/*
36
 * XXX Would like the "parameters" field to be a SECItem *, but the
37
 * encoder is having trouble with optional pointers to an ANY.  Maybe
38
 * once that is fixed, can change this back...
39
 */
40
typedef struct {
41
    SECItem capabilityID;
42
    SECItem parameters;
43
    long cipher; /* optimization */
44
} NSSSMIMECapability;
45
46
static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
47
    { SEC_ASN1_SEQUENCE,
48
      0, NULL, sizeof(NSSSMIMECapability) },
49
    { SEC_ASN1_OBJECT_ID,
50
      offsetof(NSSSMIMECapability, capabilityID) },
51
    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
52
      offsetof(NSSSMIMECapability, parameters) },
53
    { 0 }
54
};
55
56
static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
57
    { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
58
};
59
60
/*
61
 * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
62
 *  to store this and only this certificate permanently for the sender email address.
63
 */
64
typedef enum {
65
    NSSSMIMEEncryptionKeyPref_IssuerSN,
66
    NSSSMIMEEncryptionKeyPref_RKeyID,
67
    NSSSMIMEEncryptionKeyPref_SubjectKeyID
68
} NSSSMIMEEncryptionKeyPrefSelector;
69
70
typedef struct {
71
    NSSSMIMEEncryptionKeyPrefSelector selector;
72
    union {
73
        CERTIssuerAndSN *issuerAndSN;
74
        NSSCMSRecipientKeyIdentifier *recipientKeyID;
75
        SECItem *subjectKeyID;
76
    } id;
77
} NSSSMIMEEncryptionKeyPreference;
78
79
extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
80
81
static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
82
    { SEC_ASN1_CHOICE,
83
      offsetof(NSSSMIMEEncryptionKeyPreference, selector), NULL,
84
      sizeof(NSSSMIMEEncryptionKeyPreference) },
85
    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0 | SEC_ASN1_CONSTRUCTED,
86
      offsetof(NSSSMIMEEncryptionKeyPreference, id.issuerAndSN),
87
      SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
88
      NSSSMIMEEncryptionKeyPref_IssuerSN },
89
    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED,
90
      offsetof(NSSSMIMEEncryptionKeyPreference, id.recipientKeyID),
91
      NSSCMSRecipientKeyIdentifierTemplate,
92
      NSSSMIMEEncryptionKeyPref_RKeyID },
93
    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2 | SEC_ASN1_CONSTRUCTED,
94
      offsetof(NSSSMIMEEncryptionKeyPreference, id.subjectKeyID),
95
      SEC_ASN1_SUB(SEC_OctetStringTemplate),
96
      NSSSMIMEEncryptionKeyPref_SubjectKeyID },
97
    { 0 }
98
};
99
100
/* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
101
typedef struct {
102
    unsigned long cipher;
103
    SECOidTag algtag;
104
    SECItem *parms;
105
    PRBool enabled; /* in the user's preferences */
106
    PRBool allowed; /* per export policy */
107
} smime_cipher_map_entry;
108
109
/* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
110
static smime_cipher_map_entry smime_cipher_map[] = {
111
    /*    cipher, algtag, parms, enabled, allowed    */
112
    /*    ---------------------------------------    */
113
    { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_int40, PR_TRUE, PR_TRUE },
114
    { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
115
    { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_TRUE, PR_TRUE },
116
    { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
117
    { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
118
    { SMIME_AES_CBC_128, SEC_OID_AES_128_CBC, NULL, PR_TRUE, PR_TRUE },
119
    { SMIME_AES_CBC_256, SEC_OID_AES_256_CBC, NULL, PR_TRUE, PR_TRUE }
120
};
121
static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
122
123
/*
124
 * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
125
 */
126
static int
127
smime_mapi_by_cipher(unsigned long cipher)
128
0
{
129
0
    int i;
130
0
131
0
    for (i = 0; i < smime_cipher_map_count; i++) {
132
0
        if (smime_cipher_map[i].cipher == cipher)
133
0
            return i; /* bingo */
134
0
    }
135
0
    return -1; /* should not happen if we're consistent, right? */
136
0
}
137
138
/*
139
 * NSS_SMIME_EnableCipher - this function locally records the user's preference
140
 */
141
SECStatus
142
NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
143
0
{
144
0
    unsigned long mask;
145
0
    int mapi;
146
0
147
0
    mask = which & CIPHER_FAMILYID_MASK;
148
0
149
0
    PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
150
0
    if (mask != CIPHER_FAMILYID_SMIME)
151
0
        /* XXX set an error! */
152
0
        return SECFailure;
153
0
154
0
    mapi = smime_mapi_by_cipher(which);
155
0
    if (mapi < 0)
156
0
        /* XXX set an error */
157
0
        return SECFailure;
158
0
159
0
    /* do we try to turn on a forbidden cipher? */
160
0
    if (!smime_cipher_map[mapi].allowed && on) {
161
0
        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
162
0
        return SECFailure;
163
0
    }
164
0
165
0
    if (smime_cipher_map[mapi].enabled != on)
166
0
        smime_cipher_map[mapi].enabled = on;
167
0
168
0
    return SECSuccess;
169
0
}
170
171
/*
172
 * this function locally records the export policy
173
 */
174
SECStatus
175
NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
176
0
{
177
0
    unsigned long mask;
178
0
    int mapi;
179
0
180
0
    mask = which & CIPHER_FAMILYID_MASK;
181
0
182
0
    PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
183
0
    if (mask != CIPHER_FAMILYID_SMIME)
184
0
        /* XXX set an error! */
185
0
        return SECFailure;
186
0
187
0
    mapi = smime_mapi_by_cipher(which);
188
0
    if (mapi < 0)
189
0
        /* XXX set an error */
190
0
        return SECFailure;
191
0
192
0
    if (smime_cipher_map[mapi].allowed != on)
193
0
        smime_cipher_map[mapi].allowed = on;
194
0
195
0
    return SECSuccess;
196
0
}
197
198
/*
199
 * Based on the given algorithm (including its parameters, in some cases!)
200
 * and the given key (may or may not be inspected, depending on the
201
 * algorithm), find the appropriate policy algorithm specification
202
 * and return it.  If no match can be made, -1 is returned.
203
 */
204
static SECStatus
205
nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key,
206
                                     unsigned long *cipher)
207
0
{
208
0
    SECOidTag algtag;
209
0
    unsigned int keylen_bits;
210
0
    unsigned long c;
211
0
212
0
    algtag = SECOID_GetAlgorithmTag(algid);
213
0
    switch (algtag) {
214
0
        case SEC_OID_RC2_CBC:
215
0
            keylen_bits = PK11_GetKeyStrength(key, algid);
216
0
            switch (keylen_bits) {
217
0
                case 40:
218
0
                    c = SMIME_RC2_CBC_40;
219
0
                    break;
220
0
                case 64:
221
0
                    c = SMIME_RC2_CBC_64;
222
0
                    break;
223
0
                case 128:
224
0
                    c = SMIME_RC2_CBC_128;
225
0
                    break;
226
0
                default:
227
0
                    return SECFailure;
228
0
            }
229
0
            break;
230
0
        case SEC_OID_DES_CBC:
231
0
            c = SMIME_DES_CBC_56;
232
0
            break;
233
0
        case SEC_OID_DES_EDE3_CBC:
234
0
            c = SMIME_DES_EDE3_168;
235
0
            break;
236
0
        case SEC_OID_AES_128_CBC:
237
0
            c = SMIME_AES_CBC_128;
238
0
            break;
239
0
        case SEC_OID_AES_256_CBC:
240
0
            c = SMIME_AES_CBC_256;
241
0
            break;
242
0
        default:
243
0
            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
244
0
            return SECFailure;
245
0
    }
246
0
    *cipher = c;
247
0
    return SECSuccess;
248
0
}
249
250
static PRBool
251
nss_smime_cipher_allowed(unsigned long which)
252
0
{
253
0
    int mapi;
254
0
255
0
    mapi = smime_mapi_by_cipher(which);
256
0
    if (mapi < 0)
257
0
        return PR_FALSE;
258
0
    return smime_cipher_map[mapi].allowed;
259
0
}
260
261
PRBool
262
NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
263
0
{
264
0
    unsigned long which;
265
0
266
0
    if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
267
0
        return PR_FALSE;
268
0
269
0
    return nss_smime_cipher_allowed(which);
270
0
}
271
272
/*
273
 * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
274
 *
275
 * This tells whether or not *any* S/MIME encryption can be done,
276
 * according to policy.  Callers may use this to do nicer user interface
277
 * (say, greying out a checkbox so a user does not even try to encrypt
278
 * a message when they are not allowed to) or for any reason they want
279
 * to check whether S/MIME encryption (or decryption, for that matter)
280
 * may be done.
281
 *
282
 * It takes no arguments.  The return value is a simple boolean:
283
 *   PR_TRUE means encryption (or decryption) is *possible*
284
 *      (but may still fail due to other reasons, like because we cannot
285
 *      find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
286
 *   PR_FALSE means encryption (or decryption) is not permitted
287
 *
288
 * There are no errors from this routine.
289
 */
290
PRBool
291
NSS_SMIMEUtil_EncryptionPossible(void)
292
0
{
293
0
    int i;
294
0
295
0
    for (i = 0; i < smime_cipher_map_count; i++) {
296
0
        if (smime_cipher_map[i].allowed)
297
0
            return PR_TRUE;
298
0
    }
299
0
    return PR_FALSE;
300
0
}
301
302
static int
303
nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
304
0
{
305
0
    int i;
306
0
    SECOidTag capIDTag;
307
0
308
0
    /* we need the OIDTag here */
309
0
    capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
310
0
311
0
    /* go over all the SMIME ciphers we know and see if we find a match */
312
0
    for (i = 0; i < smime_cipher_map_count; i++) {
313
0
        if (smime_cipher_map[i].algtag != capIDTag)
314
0
            continue;
315
0
        /*
316
0
         * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
317
0
         * 2 NULLs as equal and NULL and non-NULL as not equal), we could
318
0
         * use that here instead of all of the following comparison code.
319
0
         */
320
0
        if (!smime_cipher_map[i].parms) {
321
0
            if (!cap->parameters.data || !cap->parameters.len)
322
0
                break; /* both empty: bingo */
323
0
            if (cap->parameters.len == 2 &&
324
0
                cap->parameters.data[0] == SEC_ASN1_NULL &&
325
0
                cap->parameters.data[1] == 0)
326
0
                break; /* DER NULL == NULL, bingo */
327
0
        } else if (cap->parameters.data != NULL &&
328
0
                   cap->parameters.len == smime_cipher_map[i].parms->len &&
329
0
                   PORT_Memcmp(cap->parameters.data, smime_cipher_map[i].parms->data,
330
0
                               cap->parameters.len) == 0) {
331
0
            break; /* both not empty, same length & equal content: bingo */
332
0
        }
333
0
    }
334
0
335
0
    if (i == smime_cipher_map_count)
336
0
        return 0;                      /* no match found */
337
0
    return smime_cipher_map[i].cipher; /* match found, point to cipher */
338
0
}
339
340
/*
341
 * smime_choose_cipher - choose a cipher that works for all the recipients
342
 *
343
 * "scert"  - sender's certificate
344
 * "rcerts" - recipient's certificates
345
 */
346
static long
347
smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
348
0
{
349
0
    PLArenaPool *poolp;
350
0
    long cipher;
351
0
    long chosen_cipher;
352
0
    int *cipher_abilities;
353
0
    int *cipher_votes;
354
0
    int weak_mapi;
355
0
    int strong_mapi;
356
0
    int aes128_mapi;
357
0
    int aes256_mapi;
358
0
    int rcount, mapi, max, i;
359
0
360
0
    chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
361
0
    weak_mapi = smime_mapi_by_cipher(chosen_cipher);
362
0
    aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
363
0
    aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
364
0
365
0
    poolp = PORT_NewArena(1024); /* XXX what is right value? */
366
0
    if (poolp == NULL)
367
0
        goto done;
368
0
369
0
    cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
370
0
    cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
371
0
    if (cipher_votes == NULL || cipher_abilities == NULL)
372
0
        goto done;
373
0
374
0
    /* Make triple-DES the strong cipher. */
375
0
    strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168);
376
0
377
0
    /* walk all the recipient's certs */
378
0
    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
379
0
        SECItem *profile;
380
0
        NSSSMIMECapability **caps;
381
0
        int pref;
382
0
383
0
        /* the first cipher that matches in the user's SMIME profile gets
384
0
         * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
385
0
         * and so on. If every cipher matches, the last one gets 1 (one) vote */
386
0
        pref = smime_cipher_map_count;
387
0
388
0
        /* find recipient's SMIME profile */
389
0
        profile = CERT_FindSMimeProfile(rcerts[rcount]);
390
0
391
0
        if (profile != NULL && profile->data != NULL && profile->len > 0) {
392
0
            /* we have a profile (still DER-encoded) */
393
0
            caps = NULL;
394
0
            /* decode it */
395
0
            if (SEC_QuickDERDecodeItem(poolp, &caps,
396
0
                                       NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
397
0
                caps != NULL) {
398
0
                /* walk the SMIME capabilities for this recipient */
399
0
                for (i = 0; caps[i] != NULL; i++) {
400
0
                    cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
401
0
                    mapi = smime_mapi_by_cipher(cipher);
402
0
                    if (mapi >= 0) {
403
0
                        /* found the cipher */
404
0
                        cipher_abilities[mapi]++;
405
0
                        cipher_votes[mapi] += pref;
406
0
                        --pref;
407
0
                    }
408
0
                }
409
0
            }
410
0
        } else {
411
0
            /* no profile found - so we can only assume that the user can do
412
0
             * the mandatory algorithms which are RC2-40 (weak crypto) and
413
0
             * 3DES (strong crypto), unless the user has an elliptic curve
414
0
             * key.  For elliptic curve keys, RFC 5753 mandates support
415
0
             * for AES 128 CBC. */
416
0
            SECKEYPublicKey *key;
417
0
            unsigned int pklen_bits;
418
0
            KeyType key_type;
419
0
420
0
            /*
421
0
             * if recipient's public key length is > 512, vote for a strong cipher
422
0
             * please not that the side effect of this is that if only one recipient
423
0
             * has an export-level public key, the strong cipher is disabled.
424
0
             *
425
0
             * XXX This is probably only good for RSA keys.  What I would
426
0
             * really like is a function to just say;  Is the public key in
427
0
             * this cert an export-length key?  Then I would not have to
428
0
             * know things like the value 512, or the kind of key, or what
429
0
             * a subjectPublicKeyInfo is, etc.
430
0
             */
431
0
            key = CERT_ExtractPublicKey(rcerts[rcount]);
432
0
            pklen_bits = 0;
433
0
            key_type = nullKey;
434
0
            if (key != NULL) {
435
0
                pklen_bits = SECKEY_PublicKeyStrengthInBits(key);
436
0
                key_type = SECKEY_GetPublicKeyType(key);
437
0
                SECKEY_DestroyPublicKey(key);
438
0
                key = NULL;
439
0
            }
440
0
441
0
            if (key_type == ecKey) {
442
0
                /* While RFC 5753 mandates support for AES-128 CBC, should use
443
0
                 * AES 256 if user's key provides more than 128 bits of
444
0
                 * security strength so that symmetric key is not weak link. */
445
0
446
0
                /* RC2-40 is not compatible with elliptic curve keys. */
447
0
                chosen_cipher = SMIME_DES_EDE3_168;
448
0
                if (pklen_bits > 256) {
449
0
                    cipher_abilities[aes256_mapi]++;
450
0
                    cipher_votes[aes256_mapi] += pref;
451
0
                    pref--;
452
0
                }
453
0
                cipher_abilities[aes128_mapi]++;
454
0
                cipher_votes[aes128_mapi] += pref;
455
0
                pref--;
456
0
                cipher_abilities[strong_mapi]++;
457
0
                cipher_votes[strong_mapi] += pref;
458
0
                pref--;
459
0
            } else {
460
0
                if (pklen_bits > 512) {
461
0
                    /* cast votes for the strong algorithm */
462
0
                    cipher_abilities[strong_mapi]++;
463
0
                    cipher_votes[strong_mapi] += pref;
464
0
                    pref--;
465
0
                }
466
0
467
0
                /* always cast (possibly less) votes for the weak algorithm */
468
0
                cipher_abilities[weak_mapi]++;
469
0
                cipher_votes[weak_mapi] += pref;
470
0
            }
471
0
        }
472
0
        if (profile != NULL)
473
0
            SECITEM_FreeItem(profile, PR_TRUE);
474
0
    }
475
0
476
0
    /* find cipher that is agreeable by all recipients and that has the most votes */
477
0
    max = 0;
478
0
    for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
479
0
        /* if not all of the recipients can do this, forget it */
480
0
        if (cipher_abilities[mapi] != rcount)
481
0
            continue;
482
0
        /* if cipher is not enabled or not allowed by policy, forget it */
483
0
        if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
484
0
            continue;
485
0
        /* now see if this one has more votes than the last best one */
486
0
        if (cipher_votes[mapi] >= max) {
487
0
            /* if equal number of votes, prefer the ones further down in the list */
488
0
            /* with the expectation that these are higher rated ciphers */
489
0
            chosen_cipher = smime_cipher_map[mapi].cipher;
490
0
            max = cipher_votes[mapi];
491
0
        }
492
0
    }
493
0
/* if no common cipher was found, chosen_cipher stays at the default */
494
0
495
0
done:
496
0
    if (poolp != NULL)
497
0
        PORT_FreeArena(poolp, PR_FALSE);
498
0
499
0
    return chosen_cipher;
500
0
}
501
502
/*
503
 * XXX This is a hack for now to satisfy our current interface.
504
 * Eventually, with more parameters needing to be specified, just
505
 * looking up the keysize is not going to be sufficient.
506
 */
507
static int
508
smime_keysize_by_cipher(unsigned long which)
509
0
{
510
0
    int keysize;
511
0
512
0
    switch (which) {
513
0
        case SMIME_RC2_CBC_40:
514
0
            keysize = 40;
515
0
            break;
516
0
        case SMIME_RC2_CBC_64:
517
0
            keysize = 64;
518
0
            break;
519
0
        case SMIME_RC2_CBC_128:
520
0
        case SMIME_AES_CBC_128:
521
0
            keysize = 128;
522
0
            break;
523
0
        case SMIME_AES_CBC_256:
524
0
            keysize = 256;
525
0
            break;
526
0
        case SMIME_DES_CBC_56:
527
0
        case SMIME_DES_EDE3_168:
528
0
            /*
529
0
         * These are special; since the key size is fixed, we actually
530
0
         * want to *avoid* specifying a key size.
531
0
         */
532
0
            keysize = 0;
533
0
            break;
534
0
        default:
535
0
            keysize = -1;
536
0
            break;
537
0
    }
538
0
539
0
    return keysize;
540
0
}
541
542
/*
543
 * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
544
 *
545
 * it would be great for UI purposes if there would be a way to find out which recipients
546
 * prevented a strong cipher from being used...
547
 */
548
SECStatus
549
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
550
                                       SECOidTag *bulkalgtag, int *keysize)
551
0
{
552
0
    unsigned long cipher;
553
0
    int mapi;
554
0
555
0
    cipher = smime_choose_cipher(NULL, rcerts);
556
0
    mapi = smime_mapi_by_cipher(cipher);
557
0
558
0
    *bulkalgtag = smime_cipher_map[mapi].algtag;
559
0
    *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
560
0
561
0
    return SECSuccess;
562
0
}
563
564
/*
565
 * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
566
 *
567
 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
568
 * S/MIME capabilities attribute value.
569
 *
570
 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
571
 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
572
 *
573
 * "poolp" - arena pool to create the S/MIME capabilities data on
574
 * "dest" - SECItem to put the data in
575
 */
576
SECStatus
577
NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
578
0
{
579
0
    NSSSMIMECapability *cap;
580
0
    NSSSMIMECapability **smime_capabilities;
581
0
    smime_cipher_map_entry *map;
582
0
    SECOidData *oiddata;
583
0
    SECItem *dummy;
584
0
    int i, capIndex;
585
0
586
0
    /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
587
0
    /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
588
0
    smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1) * sizeof(NSSSMIMECapability *));
589
0
    if (smime_capabilities == NULL)
590
0
        return SECFailure;
591
0
592
0
    capIndex = 0;
593
0
594
0
    /* Add all the symmetric ciphers
595
0
     * We walk the cipher list backwards, as it is ordered by increasing strength,
596
0
     * we prefer the stronger cipher over a weaker one, and we have to list the
597
0
     * preferred algorithm first */
598
0
    for (i = smime_cipher_map_count - 1; i >= 0; i--) {
599
0
        /* Find the corresponding entry in the cipher map. */
600
0
        map = &(smime_cipher_map[i]);
601
0
        if (!map->enabled)
602
0
            continue;
603
0
604
0
        /* get next SMIME capability */
605
0
        cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
606
0
        if (cap == NULL)
607
0
            break;
608
0
        smime_capabilities[capIndex++] = cap;
609
0
610
0
        oiddata = SECOID_FindOIDByTag(map->algtag);
611
0
        if (oiddata == NULL)
612
0
            break;
613
0
614
0
        cap->capabilityID.data = oiddata->oid.data;
615
0
        cap->capabilityID.len = oiddata->oid.len;
616
0
        cap->parameters.data = map->parms ? map->parms->data : NULL;
617
0
        cap->parameters.len = map->parms ? map->parms->len : 0;
618
0
        cap->cipher = smime_cipher_map[i].cipher;
619
0
    }
620
0
621
0
    /* XXX add signature algorithms */
622
0
    /* XXX add key encipherment algorithms */
623
0
624
0
    smime_capabilities[capIndex] = NULL; /* last one - now encode */
625
0
    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
626
0
627
0
    /* now that we have the proper encoded SMIMECapabilities (or not),
628
0
     * free the work data */
629
0
    for (i = 0; smime_capabilities[i] != NULL; i++)
630
0
        PORT_Free(smime_capabilities[i]);
631
0
    PORT_Free(smime_capabilities);
632
0
633
0
    return (dummy == NULL) ? SECFailure : SECSuccess;
634
0
}
635
636
/*
637
 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
638
 *
639
 * "poolp" - arena pool to create the attr value on
640
 * "dest" - SECItem to put the data in
641
 * "cert" - certificate that should be marked as preferred encryption key
642
 *          cert is expected to have been verified for EmailRecipient usage.
643
 */
644
SECStatus
645
NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
646
0
{
647
0
    NSSSMIMEEncryptionKeyPreference ekp;
648
0
    SECItem *dummy = NULL;
649
0
    PLArenaPool *tmppoolp = NULL;
650
0
651
0
    if (cert == NULL)
652
0
        goto loser;
653
0
654
0
    tmppoolp = PORT_NewArena(1024);
655
0
    if (tmppoolp == NULL)
656
0
        goto loser;
657
0
658
0
    /* XXX hardcoded IssuerSN choice for now */
659
0
    ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
660
0
    ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
661
0
    if (ekp.id.issuerAndSN == NULL)
662
0
        goto loser;
663
0
664
0
    dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
665
0
666
0
loser:
667
0
    if (tmppoolp)
668
0
        PORT_FreeArena(tmppoolp, PR_FALSE);
669
0
670
0
    return (dummy == NULL) ? SECFailure : SECSuccess;
671
0
}
672
673
/*
674
 * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
675
 *
676
 * "poolp" - arena pool to create the attr value on
677
 * "dest" - SECItem to put the data in
678
 * "cert" - certificate that should be marked as preferred encryption key
679
 *          cert is expected to have been verified for EmailRecipient usage.
680
 */
681
SECStatus
682
NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
683
0
{
684
0
    SECItem *dummy = NULL;
685
0
    PLArenaPool *tmppoolp = NULL;
686
0
    CERTIssuerAndSN *isn;
687
0
688
0
    if (cert == NULL)
689
0
        goto loser;
690
0
691
0
    tmppoolp = PORT_NewArena(1024);
692
0
    if (tmppoolp == NULL)
693
0
        goto loser;
694
0
695
0
    isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
696
0
    if (isn == NULL)
697
0
        goto loser;
698
0
699
0
    dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
700
0
701
0
loser:
702
0
    if (tmppoolp)
703
0
        PORT_FreeArena(tmppoolp, PR_FALSE);
704
0
705
0
    return (dummy == NULL) ? SECFailure : SECSuccess;
706
0
}
707
708
/*
709
 * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
710
 *                              find cert marked by EncryptionKeyPreference attribute
711
 *
712
 * "certdb" - handle for the cert database to look in
713
 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
714
 *
715
 * if certificate is supposed to be found among the message's included certificates,
716
 * they are assumed to have been imported already.
717
 */
718
CERTCertificate *
719
NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
720
0
{
721
0
    PLArenaPool *tmppoolp = NULL;
722
0
    CERTCertificate *cert = NULL;
723
0
    NSSSMIMEEncryptionKeyPreference ekp;
724
0
725
0
    tmppoolp = PORT_NewArena(1024);
726
0
    if (tmppoolp == NULL)
727
0
        return NULL;
728
0
729
0
    /* decode DERekp */
730
0
    if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
731
0
                               DERekp) != SECSuccess)
732
0
        goto loser;
733
0
734
0
    /* find cert */
735
0
    switch (ekp.selector) {
736
0
        case NSSSMIMEEncryptionKeyPref_IssuerSN:
737
0
            cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
738
0
            break;
739
0
        case NSSSMIMEEncryptionKeyPref_RKeyID:
740
0
        case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
741
0
            /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
742
0
            break;
743
0
        default:
744
0
            PORT_Assert(0);
745
0
    }
746
0
loser:
747
0
    if (tmppoolp)
748
0
        PORT_FreeArena(tmppoolp, PR_FALSE);
749
0
750
0
    return cert;
751
0
}
752
753
extern const char __nss_smime_version[];
754
755
PRBool
756
NSSSMIME_VersionCheck(const char *importedVersion)
757
0
{
758
0
#define NSS_VERSION_VARIABLE __nss_smime_version
759
0
#include "verref.h"
760
0
    /*
761
0
     * This is the secret handshake algorithm.
762
0
     *
763
0
     * This release has a simple version compatibility
764
0
     * check algorithm.  This release is not backward
765
0
     * compatible with previous major releases.  It is
766
0
     * not compatible with future major, minor, or
767
0
     * patch releases.
768
0
     */
769
0
    return NSS_VersionCheck(importedVersion);
770
0
}
771
772
const char *
773
NSSSMIME_GetVersion(void)
774
0
{
775
0
    return NSS_VERSION;
776
0
}