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
smime_mapi_by_cipher(unsigned long cipher)127 smime_mapi_by_cipher(unsigned long cipher)
128 {
129     int i;
130 
131     for (i = 0; i < smime_cipher_map_count; i++) {
132         if (smime_cipher_map[i].cipher == cipher)
133             return i; /* bingo */
134     }
135     return -1; /* should not happen if we're consistent, right? */
136 }
137 
138 /*
139  * NSS_SMIME_EnableCipher - this function locally records the user's preference
140  */
141 SECStatus
NSS_SMIMEUtil_EnableCipher(unsigned long which,PRBool on)142 NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
143 {
144     unsigned long mask;
145     int mapi;
146 
147     mask = which & CIPHER_FAMILYID_MASK;
148 
149     PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
150     if (mask != CIPHER_FAMILYID_SMIME)
151         /* XXX set an error! */
152         return SECFailure;
153 
154     mapi = smime_mapi_by_cipher(which);
155     if (mapi < 0)
156         /* XXX set an error */
157         return SECFailure;
158 
159     /* do we try to turn on a forbidden cipher? */
160     if (!smime_cipher_map[mapi].allowed && on) {
161         PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
162         return SECFailure;
163     }
164 
165     if (smime_cipher_map[mapi].enabled != on)
166         smime_cipher_map[mapi].enabled = on;
167 
168     return SECSuccess;
169 }
170 
171 /*
172  * this function locally records the export policy
173  */
174 SECStatus
NSS_SMIMEUtil_AllowCipher(unsigned long which,PRBool on)175 NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
176 {
177     unsigned long mask;
178     int mapi;
179 
180     mask = which & CIPHER_FAMILYID_MASK;
181 
182     PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
183     if (mask != CIPHER_FAMILYID_SMIME)
184         /* XXX set an error! */
185         return SECFailure;
186 
187     mapi = smime_mapi_by_cipher(which);
188     if (mapi < 0)
189         /* XXX set an error */
190         return SECFailure;
191 
192     if (smime_cipher_map[mapi].allowed != on)
193         smime_cipher_map[mapi].allowed = on;
194 
195     return SECSuccess;
196 }
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
nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID * algid,PK11SymKey * key,unsigned long * cipher)205 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key,
206                                      unsigned long *cipher)
207 {
208     SECOidTag algtag;
209     unsigned int keylen_bits;
210     unsigned long c;
211 
212     algtag = SECOID_GetAlgorithmTag(algid);
213     switch (algtag) {
214         case SEC_OID_RC2_CBC:
215             keylen_bits = PK11_GetKeyStrength(key, algid);
216             switch (keylen_bits) {
217                 case 40:
218                     c = SMIME_RC2_CBC_40;
219                     break;
220                 case 64:
221                     c = SMIME_RC2_CBC_64;
222                     break;
223                 case 128:
224                     c = SMIME_RC2_CBC_128;
225                     break;
226                 default:
227                     return SECFailure;
228             }
229             break;
230         case SEC_OID_DES_CBC:
231             c = SMIME_DES_CBC_56;
232             break;
233         case SEC_OID_DES_EDE3_CBC:
234             c = SMIME_DES_EDE3_168;
235             break;
236         case SEC_OID_AES_128_CBC:
237             c = SMIME_AES_CBC_128;
238             break;
239         case SEC_OID_AES_256_CBC:
240             c = SMIME_AES_CBC_256;
241             break;
242         default:
243             PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
244             return SECFailure;
245     }
246     *cipher = c;
247     return SECSuccess;
248 }
249 
250 static PRBool
nss_smime_cipher_allowed(unsigned long which)251 nss_smime_cipher_allowed(unsigned long which)
252 {
253     int mapi;
254 
255     mapi = smime_mapi_by_cipher(which);
256     if (mapi < 0)
257         return PR_FALSE;
258     return smime_cipher_map[mapi].allowed;
259 }
260 
261 PRBool
NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID * algid,PK11SymKey * key)262 NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
263 {
264     unsigned long which;
265 
266     if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
267         return PR_FALSE;
268 
269     return nss_smime_cipher_allowed(which);
270 }
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
NSS_SMIMEUtil_EncryptionPossible(void)291 NSS_SMIMEUtil_EncryptionPossible(void)
292 {
293     int i;
294 
295     for (i = 0; i < smime_cipher_map_count; i++) {
296         if (smime_cipher_map[i].allowed)
297             return PR_TRUE;
298     }
299     return PR_FALSE;
300 }
301 
302 static int
nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability * cap)303 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
304 {
305     int i;
306     SECOidTag capIDTag;
307 
308     /* we need the OIDTag here */
309     capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
310 
311     /* go over all the SMIME ciphers we know and see if we find a match */
312     for (i = 0; i < smime_cipher_map_count; i++) {
313         if (smime_cipher_map[i].algtag != capIDTag)
314             continue;
315         /*
316          * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
317          * 2 NULLs as equal and NULL and non-NULL as not equal), we could
318          * use that here instead of all of the following comparison code.
319          */
320         if (!smime_cipher_map[i].parms) {
321             if (!cap->parameters.data || !cap->parameters.len)
322                 break; /* both empty: bingo */
323             if (cap->parameters.len == 2 &&
324                 cap->parameters.data[0] == SEC_ASN1_NULL &&
325                 cap->parameters.data[1] == 0)
326                 break; /* DER NULL == NULL, bingo */
327         } else if (cap->parameters.data != NULL &&
328                    cap->parameters.len == smime_cipher_map[i].parms->len &&
329                    PORT_Memcmp(cap->parameters.data, smime_cipher_map[i].parms->data,
330                                cap->parameters.len) == 0) {
331             break; /* both not empty, same length & equal content: bingo */
332         }
333     }
334 
335     if (i == smime_cipher_map_count)
336         return 0;                      /* no match found */
337     return smime_cipher_map[i].cipher; /* match found, point to cipher */
338 }
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
smime_choose_cipher(CERTCertificate * scert,CERTCertificate ** rcerts)347 smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
348 {
349     PLArenaPool *poolp;
350     long cipher;
351     long chosen_cipher;
352     int *cipher_abilities;
353     int *cipher_votes;
354     int weak_mapi;
355     int strong_mapi;
356     int aes128_mapi;
357     int aes256_mapi;
358     int rcount, mapi, max, i;
359 
360     chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
361     weak_mapi = smime_mapi_by_cipher(chosen_cipher);
362     aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
363     aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
364 
365     poolp = PORT_NewArena(1024); /* XXX what is right value? */
366     if (poolp == NULL)
367         goto done;
368 
369     cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
370     cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
371     if (cipher_votes == NULL || cipher_abilities == NULL)
372         goto done;
373 
374     /* Make triple-DES the strong cipher. */
375     strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168);
376 
377     /* walk all the recipient's certs */
378     for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
379         SECItem *profile;
380         NSSSMIMECapability **caps;
381         int pref;
382 
383         /* the first cipher that matches in the user's SMIME profile gets
384          * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
385          * and so on. If every cipher matches, the last one gets 1 (one) vote */
386         pref = smime_cipher_map_count;
387 
388         /* find recipient's SMIME profile */
389         profile = CERT_FindSMimeProfile(rcerts[rcount]);
390 
391         if (profile != NULL && profile->data != NULL && profile->len > 0) {
392             /* we have a profile (still DER-encoded) */
393             caps = NULL;
394             /* decode it */
395             if (SEC_QuickDERDecodeItem(poolp, &caps,
396                                        NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
397                 caps != NULL) {
398                 /* walk the SMIME capabilities for this recipient */
399                 for (i = 0; caps[i] != NULL; i++) {
400                     cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
401                     mapi = smime_mapi_by_cipher(cipher);
402                     if (mapi >= 0) {
403                         /* found the cipher */
404                         cipher_abilities[mapi]++;
405                         cipher_votes[mapi] += pref;
406                         --pref;
407                     }
408                 }
409             }
410         } else {
411             /* no profile found - so we can only assume that the user can do
412              * the mandatory algorithms which are RC2-40 (weak crypto) and
413              * 3DES (strong crypto), unless the user has an elliptic curve
414              * key.  For elliptic curve keys, RFC 5753 mandates support
415              * for AES 128 CBC. */
416             SECKEYPublicKey *key;
417             unsigned int pklen_bits;
418             KeyType key_type;
419 
420             /*
421              * if recipient's public key length is > 512, vote for a strong cipher
422              * please not that the side effect of this is that if only one recipient
423              * has an export-level public key, the strong cipher is disabled.
424              *
425              * XXX This is probably only good for RSA keys.  What I would
426              * really like is a function to just say;  Is the public key in
427              * this cert an export-length key?  Then I would not have to
428              * know things like the value 512, or the kind of key, or what
429              * a subjectPublicKeyInfo is, etc.
430              */
431             key = CERT_ExtractPublicKey(rcerts[rcount]);
432             pklen_bits = 0;
433             key_type = nullKey;
434             if (key != NULL) {
435                 pklen_bits = SECKEY_PublicKeyStrengthInBits(key);
436                 key_type = SECKEY_GetPublicKeyType(key);
437                 SECKEY_DestroyPublicKey(key);
438                 key = NULL;
439             }
440 
441             if (key_type == ecKey) {
442                 /* While RFC 5753 mandates support for AES-128 CBC, should use
443                  * AES 256 if user's key provides more than 128 bits of
444                  * security strength so that symmetric key is not weak link. */
445 
446                 /* RC2-40 is not compatible with elliptic curve keys. */
447                 chosen_cipher = SMIME_DES_EDE3_168;
448                 if (pklen_bits > 256) {
449                     cipher_abilities[aes256_mapi]++;
450                     cipher_votes[aes256_mapi] += pref;
451                     pref--;
452                 }
453                 cipher_abilities[aes128_mapi]++;
454                 cipher_votes[aes128_mapi] += pref;
455                 pref--;
456                 cipher_abilities[strong_mapi]++;
457                 cipher_votes[strong_mapi] += pref;
458                 pref--;
459             } else {
460                 if (pklen_bits > 3072) {
461                     /* While support for AES 256 is a SHOULD+ in RFC 5751
462                      * rather than a MUST, RSA and DSA keys longer than 3072
463                      * bits provide more than 128 bits of security strength.
464                      * So, AES 256 should be used to provide comparable
465                      * security. */
466                     cipher_abilities[aes256_mapi]++;
467                     cipher_votes[aes256_mapi] += pref;
468                     pref--;
469                 }
470                 if (pklen_bits > 1023) {
471                     /* RFC 5751 mandates support for AES 128, but also says
472                      * that RSA and DSA signature keys SHOULD NOT be less than
473                      * 1024 bits. So, cast vote for AES 128 if key length
474                      * is at least 1024 bits. */
475                     cipher_abilities[aes128_mapi]++;
476                     cipher_votes[aes128_mapi] += pref;
477                     pref--;
478                 }
479                 if (pklen_bits > 512) {
480                     /* cast votes for the strong algorithm */
481                     cipher_abilities[strong_mapi]++;
482                     cipher_votes[strong_mapi] += pref;
483                     pref--;
484                 }
485 
486                 /* always cast (possibly less) votes for the weak algorithm */
487                 cipher_abilities[weak_mapi]++;
488                 cipher_votes[weak_mapi] += pref;
489             }
490         }
491         if (profile != NULL)
492             SECITEM_FreeItem(profile, PR_TRUE);
493     }
494 
495     /* find cipher that is agreeable by all recipients and that has the most votes */
496     max = 0;
497     for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
498         /* if not all of the recipients can do this, forget it */
499         if (cipher_abilities[mapi] != rcount)
500             continue;
501         /* if cipher is not enabled or not allowed by policy, forget it */
502         if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
503             continue;
504         /* now see if this one has more votes than the last best one */
505         if (cipher_votes[mapi] >= max) {
506             /* if equal number of votes, prefer the ones further down in the list */
507             /* with the expectation that these are higher rated ciphers */
508             chosen_cipher = smime_cipher_map[mapi].cipher;
509             max = cipher_votes[mapi];
510         }
511     }
512 /* if no common cipher was found, chosen_cipher stays at the default */
513 
514 done:
515     if (poolp != NULL)
516         PORT_FreeArena(poolp, PR_FALSE);
517 
518     return chosen_cipher;
519 }
520 
521 /*
522  * XXX This is a hack for now to satisfy our current interface.
523  * Eventually, with more parameters needing to be specified, just
524  * looking up the keysize is not going to be sufficient.
525  */
526 static int
smime_keysize_by_cipher(unsigned long which)527 smime_keysize_by_cipher(unsigned long which)
528 {
529     int keysize;
530 
531     switch (which) {
532         case SMIME_RC2_CBC_40:
533             keysize = 40;
534             break;
535         case SMIME_RC2_CBC_64:
536             keysize = 64;
537             break;
538         case SMIME_RC2_CBC_128:
539         case SMIME_AES_CBC_128:
540             keysize = 128;
541             break;
542         case SMIME_AES_CBC_256:
543             keysize = 256;
544             break;
545         case SMIME_DES_CBC_56:
546         case SMIME_DES_EDE3_168:
547             /*
548          * These are special; since the key size is fixed, we actually
549          * want to *avoid* specifying a key size.
550          */
551             keysize = 0;
552             break;
553         default:
554             keysize = -1;
555             break;
556     }
557 
558     return keysize;
559 }
560 
561 /*
562  * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
563  *
564  * it would be great for UI purposes if there would be a way to find out which recipients
565  * prevented a strong cipher from being used...
566  */
567 SECStatus
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate ** rcerts,SECOidTag * bulkalgtag,int * keysize)568 NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
569                                        SECOidTag *bulkalgtag, int *keysize)
570 {
571     unsigned long cipher;
572     int mapi;
573 
574     cipher = smime_choose_cipher(NULL, rcerts);
575     mapi = smime_mapi_by_cipher(cipher);
576 
577     *bulkalgtag = smime_cipher_map[mapi].algtag;
578     *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
579 
580     return SECSuccess;
581 }
582 
583 /*
584  * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
585  *
586  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
587  * S/MIME capabilities attribute value.
588  *
589  * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
590  * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
591  *
592  * "poolp" - arena pool to create the S/MIME capabilities data on
593  * "dest" - SECItem to put the data in
594  */
595 SECStatus
NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool * poolp,SECItem * dest)596 NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
597 {
598     NSSSMIMECapability *cap;
599     NSSSMIMECapability **smime_capabilities;
600     smime_cipher_map_entry *map;
601     SECOidData *oiddata;
602     SECItem *dummy;
603     int i, capIndex;
604 
605     /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
606     /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
607     smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1) * sizeof(NSSSMIMECapability *));
608     if (smime_capabilities == NULL)
609         return SECFailure;
610 
611     capIndex = 0;
612 
613     /* Add all the symmetric ciphers
614      * We walk the cipher list backwards, as it is ordered by increasing strength,
615      * we prefer the stronger cipher over a weaker one, and we have to list the
616      * preferred algorithm first */
617     for (i = smime_cipher_map_count - 1; i >= 0; i--) {
618         /* Find the corresponding entry in the cipher map. */
619         map = &(smime_cipher_map[i]);
620         if (!map->enabled)
621             continue;
622 
623         /* get next SMIME capability */
624         cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
625         if (cap == NULL)
626             break;
627         smime_capabilities[capIndex++] = cap;
628 
629         oiddata = SECOID_FindOIDByTag(map->algtag);
630         if (oiddata == NULL)
631             break;
632 
633         cap->capabilityID.data = oiddata->oid.data;
634         cap->capabilityID.len = oiddata->oid.len;
635         cap->parameters.data = map->parms ? map->parms->data : NULL;
636         cap->parameters.len = map->parms ? map->parms->len : 0;
637         cap->cipher = smime_cipher_map[i].cipher;
638     }
639 
640     /* XXX add signature algorithms */
641     /* XXX add key encipherment algorithms */
642 
643     smime_capabilities[capIndex] = NULL; /* last one - now encode */
644     dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
645 
646     /* now that we have the proper encoded SMIMECapabilities (or not),
647      * free the work data */
648     for (i = 0; smime_capabilities[i] != NULL; i++)
649         PORT_Free(smime_capabilities[i]);
650     PORT_Free(smime_capabilities);
651 
652     return (dummy == NULL) ? SECFailure : SECSuccess;
653 }
654 
655 /*
656  * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
657  *
658  * "poolp" - arena pool to create the attr value on
659  * "dest" - SECItem to put the data in
660  * "cert" - certificate that should be marked as preferred encryption key
661  *          cert is expected to have been verified for EmailRecipient usage.
662  */
663 SECStatus
NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool * poolp,SECItem * dest,CERTCertificate * cert)664 NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
665 {
666     NSSSMIMEEncryptionKeyPreference ekp;
667     SECItem *dummy = NULL;
668     PLArenaPool *tmppoolp = NULL;
669 
670     if (cert == NULL)
671         goto loser;
672 
673     tmppoolp = PORT_NewArena(1024);
674     if (tmppoolp == NULL)
675         goto loser;
676 
677     /* XXX hardcoded IssuerSN choice for now */
678     ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
679     ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
680     if (ekp.id.issuerAndSN == NULL)
681         goto loser;
682 
683     dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
684 
685 loser:
686     if (tmppoolp)
687         PORT_FreeArena(tmppoolp, PR_FALSE);
688 
689     return (dummy == NULL) ? SECFailure : SECSuccess;
690 }
691 
692 /*
693  * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
694  *
695  * "poolp" - arena pool to create the attr value on
696  * "dest" - SECItem to put the data in
697  * "cert" - certificate that should be marked as preferred encryption key
698  *          cert is expected to have been verified for EmailRecipient usage.
699  */
700 SECStatus
NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool * poolp,SECItem * dest,CERTCertificate * cert)701 NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
702 {
703     SECItem *dummy = NULL;
704     PLArenaPool *tmppoolp = NULL;
705     CERTIssuerAndSN *isn;
706 
707     if (cert == NULL)
708         goto loser;
709 
710     tmppoolp = PORT_NewArena(1024);
711     if (tmppoolp == NULL)
712         goto loser;
713 
714     isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
715     if (isn == NULL)
716         goto loser;
717 
718     dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
719 
720 loser:
721     if (tmppoolp)
722         PORT_FreeArena(tmppoolp, PR_FALSE);
723 
724     return (dummy == NULL) ? SECFailure : SECSuccess;
725 }
726 
727 /*
728  * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
729  *                              find cert marked by EncryptionKeyPreference attribute
730  *
731  * "certdb" - handle for the cert database to look in
732  * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
733  *
734  * if certificate is supposed to be found among the message's included certificates,
735  * they are assumed to have been imported already.
736  */
737 CERTCertificate *
NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle * certdb,SECItem * DERekp)738 NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
739 {
740     PLArenaPool *tmppoolp = NULL;
741     CERTCertificate *cert = NULL;
742     NSSSMIMEEncryptionKeyPreference ekp;
743 
744     tmppoolp = PORT_NewArena(1024);
745     if (tmppoolp == NULL)
746         return NULL;
747 
748     /* decode DERekp */
749     if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
750                                DERekp) != SECSuccess)
751         goto loser;
752 
753     /* find cert */
754     switch (ekp.selector) {
755         case NSSSMIMEEncryptionKeyPref_IssuerSN:
756             cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
757             break;
758         case NSSSMIMEEncryptionKeyPref_RKeyID:
759         case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
760             /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
761             break;
762         default:
763             PORT_Assert(0);
764     }
765 loser:
766     if (tmppoolp)
767         PORT_FreeArena(tmppoolp, PR_FALSE);
768 
769     return cert;
770 }
771 
772 extern const char __nss_smime_version[];
773 
774 PRBool
NSSSMIME_VersionCheck(const char * importedVersion)775 NSSSMIME_VersionCheck(const char *importedVersion)
776 {
777 #define NSS_VERSION_VARIABLE __nss_smime_version
778 #include "verref.h"
779     /*
780      * This is the secret handshake algorithm.
781      *
782      * This release has a simple version compatibility
783      * check algorithm.  This release is not backward
784      * compatible with previous major releases.  It is
785      * not compatible with future major, minor, or
786      * patch releases.
787      */
788     return NSS_VersionCheck(importedVersion);
789 }
790 
791 const char *
NSSSMIME_GetVersion(void)792 NSSSMIME_GetVersion(void)
793 {
794     return NSS_VERSION;
795 }
796