1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is the Netscape security libraries.
16
* The Initial Developer of the Original Code is
17
* Netscape Communications Corporation.
18
* Portions created by the Initial Developer are Copyright (C) 1994-2000
19
* the Initial Developer. All Rights Reserved.
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
35
* ***** END LICENSE BLOCK ***** */
38
* Stuff specific to S/MIME policy and interoperability.
39
* Depends on PKCS7, but there should be no dependency the other way around.
41
* $Id: secmime.c,v 1.4 2004/06/18 00:38:45 jpierre%netscape.com Exp $
47
#include "ciferfam.h" /* for CIPHER_FAMILY symbols */
54
typedef struct smime_cipher_map_struct {
61
* These are macros because I think some subsequent parameters,
62
* like those for RC5, will want to use them, too, separately.
64
#define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10
65
#define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28
66
#define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40
67
#define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80
69
#ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */
70
static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };
72
static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };
73
static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };
74
static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };
76
static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };
77
static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };
78
static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };
80
static smime_cipher_map smime_cipher_maps[] = {
81
{ SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 },
82
{ SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 },
83
{ SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 },
85
{ SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 },
86
{ SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 },
87
{ SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 },
89
{ SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL },
90
{ SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL },
91
{ SMIME_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL}
95
* Note, the following value really just needs to be an upper bound
98
static const int smime_symmetric_count = sizeof(smime_cipher_maps)
99
/ sizeof(smime_cipher_map);
101
static unsigned long *smime_prefs, *smime_newprefs;
102
static int smime_current_pref_index = 0;
103
static PRBool smime_prefs_complete = PR_FALSE;
104
static PRBool smime_prefs_changed = PR_TRUE;
106
static unsigned long smime_policy_bits = 0;
110
smime_mapi_by_cipher (unsigned long cipher)
114
for (i = 0; i < smime_symmetric_count; i++) {
115
if (smime_cipher_maps[i].cipher == cipher)
119
if (i == smime_symmetric_count)
127
* this function locally records the user's preference
130
SECMIME_EnableCipher(long which, int on)
134
if (smime_newprefs == NULL || smime_prefs_complete) {
136
* This is either the very first time, or we are starting over.
138
smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count
139
* sizeof(*smime_newprefs));
140
if (smime_newprefs == NULL)
142
smime_current_pref_index = 0;
143
smime_prefs_complete = PR_FALSE;
146
mask = which & CIPHER_FAMILYID_MASK;
147
if (mask == CIPHER_FAMILYID_MASK) {
149
* This call signifies that all preferences have been set.
150
* Move "newprefs" over, after checking first whether or
151
* not the new ones are different from the old ones.
153
if (smime_prefs != NULL) {
154
if (PORT_Memcmp (smime_prefs, smime_newprefs,
155
smime_symmetric_count * sizeof(*smime_prefs)) == 0)
156
smime_prefs_changed = PR_FALSE;
158
smime_prefs_changed = PR_TRUE;
159
PORT_Free (smime_prefs);
162
smime_prefs = smime_newprefs;
163
smime_prefs_complete = PR_TRUE;
167
PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
168
if (mask != CIPHER_FAMILYID_SMIME) {
169
/* XXX set an error! */
174
PORT_Assert (smime_current_pref_index < smime_symmetric_count);
175
if (smime_current_pref_index >= smime_symmetric_count) {
176
/* XXX set an error! */
180
smime_newprefs[smime_current_pref_index++] = which;
188
* this function locally records the export policy
191
SECMIME_SetPolicy(long which, int on)
195
PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
196
if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
197
/* XXX set an error! */
201
which &= ~CIPHER_FAMILYID_MASK;
203
PORT_Assert (which < 32); /* bits in the long */
205
/* XXX set an error! */
212
smime_policy_bits |= mask;
214
smime_policy_bits &= ~mask;
222
* Based on the given algorithm (including its parameters, in some cases!)
223
* and the given key (may or may not be inspected, depending on the
224
* algorithm), find the appropriate policy algorithm specification
225
* and return it. If no match can be made, -1 is returned.
228
smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key)
232
algtag = SECOID_GetAlgorithmTag (algid);
234
case SEC_OID_RC2_CBC:
236
unsigned int keylen_bits;
238
keylen_bits = PK11_GetKeyStrength (key, algid);
239
switch (keylen_bits) {
241
return SMIME_RC2_CBC_40;
243
return SMIME_RC2_CBC_64;
245
return SMIME_RC2_CBC_128;
251
case SEC_OID_DES_CBC:
252
return SMIME_DES_CBC_56;
253
case SEC_OID_DES_EDE3_CBC:
254
return SMIME_DES_EDE3_168;
255
case SEC_OID_FORTEZZA_SKIPJACK:
256
return SMIME_FORTEZZA;
257
#ifdef SMIME_DOES_RC5
258
case SEC_OID_RC5_CBC_PAD:
259
PORT_Assert (0); /* XXX need to pull out parameters and match */
271
smime_cipher_allowed (unsigned long which)
275
which &= ~CIPHER_FAMILYID_MASK;
276
PORT_Assert (which < 32); /* bits per long (min) */
281
if ((mask & smime_policy_bits) == 0)
289
SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
293
which = smime_policy_algorithm (algid, key);
297
return smime_cipher_allowed ((unsigned long)which);
302
* Does the current policy allow *any* S/MIME encryption (or decryption)?
304
* This tells whether or not *any* S/MIME encryption can be done,
305
* according to policy. Callers may use this to do nicer user interface
306
* (say, greying out a checkbox so a user does not even try to encrypt
307
* a message when they are not allowed to) or for any reason they want
308
* to check whether S/MIME encryption (or decryption, for that matter)
311
* It takes no arguments. The return value is a simple boolean:
312
* PR_TRUE means encryption (or decryption) is *possible*
313
* (but may still fail due to other reasons, like because we cannot
314
* find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
315
* PR_FALSE means encryption (or decryption) is not permitted
317
* There are no errors from this routine.
320
SECMIME_EncryptionPossible (void)
322
if (smime_policy_bits != 0)
330
* XXX Would like the "parameters" field to be a SECItem *, but the
331
* encoder is having trouble with optional pointers to an ANY. Maybe
332
* once that is fixed, can change this back...
334
typedef struct smime_capability_struct {
335
unsigned long cipher; /* local; not part of encoding */
336
SECOidTag capIDTag; /* local; not part of encoding */
337
SECItem capabilityID;
341
static const SEC_ASN1Template smime_capability_template[] = {
343
0, NULL, sizeof(smime_capability) },
344
{ SEC_ASN1_OBJECT_ID,
345
offsetof(smime_capability,capabilityID), },
346
{ SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
347
offsetof(smime_capability,parameters), },
351
static const SEC_ASN1Template smime_capabilities_template[] = {
352
{ SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
358
smime_fill_capability (smime_capability *cap)
360
unsigned long cipher;
364
algtag = SECOID_FindOIDTag (&(cap->capabilityID));
366
for (i = 0; i < smime_symmetric_count; i++) {
367
if (smime_cipher_maps[i].algtag != algtag)
370
* XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
371
* 2 NULLs as equal and NULL and non-NULL as not equal), we could
372
* use that here instead of all of the following comparison code.
374
if (cap->parameters.data != NULL) {
375
if (smime_cipher_maps[i].parms == NULL)
377
if (cap->parameters.len != smime_cipher_maps[i].parms->len)
379
if (PORT_Memcmp (cap->parameters.data,
380
smime_cipher_maps[i].parms->data,
381
cap->parameters.len) == 0)
383
} else if (smime_cipher_maps[i].parms == NULL) {
388
if (i == smime_symmetric_count)
391
cipher = smime_cipher_maps[i].cipher;
393
cap->cipher = cipher;
394
cap->capIDTag = algtag;
399
smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts)
403
int *cipher_abilities;
406
int rcount, mapi, max, i;
407
PRBool isFortezza = PK11_FortezzaHasKEA(scert);
409
if (smime_policy_bits == 0) {
410
PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
414
chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
416
poolp = PORT_NewArena (1024); /* XXX what is right value? */
420
cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,
421
smime_symmetric_count * sizeof(int));
422
if (cipher_abilities == NULL)
425
cipher_votes = (int*)PORT_ArenaZAlloc (poolp,
426
smime_symmetric_count * sizeof(int));
427
if (cipher_votes == NULL)
431
* XXX Should have a #define somewhere which specifies default
432
* strong cipher. (Or better, a way to configure, which would
433
* take Fortezza into account as well.)
436
/* If the user has the Fortezza preference turned on, make
437
* that the strong cipher. Otherwise, use triple-DES. */
440
for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++)
442
if (smime_prefs[i] == SMIME_FORTEZZA)
443
strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
447
if (strong_mapi == -1)
448
strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
450
PORT_Assert (strong_mapi >= 0);
452
for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
454
smime_capability **caps;
458
pref = smime_symmetric_count;
459
profile = CERT_FindSMimeProfile (rcerts[rcount]);
460
if (profile != NULL && profile->data != NULL && profile->len > 0) {
462
dstat = SEC_QuickDERDecodeItem (poolp, &caps,
463
smime_capabilities_template,
465
if (dstat == SECSuccess && caps != NULL) {
466
for (capi = 0; caps[capi] != NULL; capi++) {
467
smime_fill_capability (caps[capi]);
468
mapi = smime_mapi_by_cipher (caps[capi]->cipher);
470
cipher_abilities[mapi]++;
471
cipher_votes[mapi] += pref;
477
SECKEYPublicKey *key;
478
unsigned int pklen_bits;
481
* XXX This is probably only good for RSA keys. What I would
482
* really like is a function to just say; Is the public key in
483
* this cert an export-length key? Then I would not have to
484
* know things like the value 512, or the kind of key, or what
485
* a subjectPublicKeyInfo is, etc.
487
key = CERT_ExtractPublicKey (rcerts[rcount]);
489
pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
490
SECKEY_DestroyPublicKey (key);
492
if (pklen_bits > 512) {
493
cipher_abilities[strong_mapi]++;
494
cipher_votes[strong_mapi] += pref;
499
SECITEM_FreeItem (profile, PR_TRUE);
503
for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
504
if (cipher_abilities[mapi] != rcount)
506
if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher))
508
if (!isFortezza && (smime_cipher_maps[mapi].cipher == SMIME_FORTEZZA))
510
if (cipher_votes[mapi] > max) {
511
chosen_cipher = smime_cipher_maps[mapi].cipher;
512
max = cipher_votes[mapi];
513
} /* XXX else if a tie, let scert break it? */
518
PORT_FreeArena (poolp, PR_FALSE);
520
return chosen_cipher;
525
* XXX This is a hack for now to satisfy our current interface.
526
* Eventually, with more parameters needing to be specified, just
527
* looking up the keysize is not going to be sufficient.
530
smime_keysize_by_cipher (unsigned long which)
535
case SMIME_RC2_CBC_40:
538
case SMIME_RC2_CBC_64:
541
case SMIME_RC2_CBC_128:
544
#ifdef SMIME_DOES_RC5
545
case SMIME_RC5PAD_64_16_40:
546
case SMIME_RC5PAD_64_16_64:
547
case SMIME_RC5PAD_64_16_128:
548
/* XXX See comment above; keysize is not enough... */
550
PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
554
case SMIME_DES_CBC_56:
555
case SMIME_DES_EDE3_168:
558
* These are special; since the key size is fixed, we actually
559
* want to *avoid* specifying a key size.
573
* Start an S/MIME encrypting context.
575
* "scert" is the cert for the sender. It will be checked for validity.
576
* "rcerts" are the certs for the recipients. They will also be checked.
578
* "certdb" is the cert database to use for verifying the certs.
579
* It can be NULL if a default database is available (like in the client).
581
* This function already does all of the stuff specific to S/MIME protocol
582
* and local policy; the return value just needs to be passed to
583
* SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
584
* and finally to SEC_PKCS7DestroyContentInfo().
586
* An error results in a return value of NULL and an error set.
587
* (Retrieve specific errors via PORT_GetError()/XP_GetError().)
589
SEC_PKCS7ContentInfo *
590
SECMIME_CreateEncrypted(CERTCertificate *scert,
591
CERTCertificate **rcerts,
592
CERTCertDBHandle *certdb,
593
SECKEYGetPasswordKey pwfn,
596
SEC_PKCS7ContentInfo *cinfo;
602
cipher = smime_choose_cipher (scert, rcerts);
606
mapi = smime_mapi_by_cipher (cipher);
611
* XXX This is stretching it -- CreateEnvelopedData should probably
612
* take a cipher itself of some sort, because we cannot know what the
613
* future will bring in terms of parameters for each type of algorithm.
614
* For example, just an algorithm and keysize is *not* sufficient to
615
* fully specify the usage of RC5 (which also needs to know rounds and
616
* block size). Work this out into a better API!
618
encalg = smime_cipher_maps[mapi].algtag;
619
keysize = smime_keysize_by_cipher (cipher);
623
cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
624
certdb, encalg, keysize,
629
for (rci = 0; rcerts[rci] != NULL; rci++) {
630
if (rcerts[rci] == scert)
632
if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
633
NULL) != SECSuccess) {
634
SEC_PKCS7DestroyContentInfo (cinfo);
643
static smime_capability **smime_capabilities;
644
static SECItem *smime_encoded_caps;
645
static PRBool lastUsedFortezza;
649
smime_init_caps (PRBool isFortezza)
651
smime_capability *cap;
652
smime_cipher_map *map;
657
if (smime_encoded_caps != NULL
658
&& (! smime_prefs_changed)
659
&& lastUsedFortezza == isFortezza)
662
if (smime_encoded_caps != NULL) {
663
SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
664
smime_encoded_caps = NULL;
667
if (smime_capabilities == NULL) {
668
smime_capabilities = (smime_capability**)PORT_ZAlloc (
669
(smime_symmetric_count + 1)
670
* sizeof(smime_capability *));
671
if (smime_capabilities == NULL)
678
The process of creating the encoded PKCS7 cipher capability list
679
involves two basic steps:
681
(a) Convert our internal representation of cipher preferences
682
(smime_prefs) into an array containing cipher OIDs and
683
parameter data (smime_capabilities). This step is
686
(b) Encode, using ASN.1, the cipher information in
687
smime_capabilities, leaving the encoded result in
690
(In the process of performing (a), Lisa put in some optimizations
691
which allow us to avoid needlessly re-populating elements in
692
smime_capabilities as we walk through smime_prefs.)
694
We want to use separate loop variables for smime_prefs and
695
smime_capabilities because in the case where the Skipjack cipher
696
is turned on in the prefs, but where we don't want to include
697
Skipjack in the encoded capabilities (presumably due to using a
698
non-fortezza cert when sending a message), we want to avoid creating
699
an empty element in smime_capabilities. This would otherwise cause
700
the encoding step to produce an empty set, since Skipjack happens
701
to be the first cipher in smime_prefs, if it is turned on.
703
for (i = 0, capIndex = 0; i < smime_current_pref_index; i++, capIndex++) {
706
/* Get the next cipher preference in smime_prefs. */
707
mapi = smime_mapi_by_cipher (smime_prefs[i]);
711
/* Find the corresponding entry in the cipher map. */
712
PORT_Assert (mapi < smime_symmetric_count);
713
map = &(smime_cipher_maps[mapi]);
715
/* If we're using a non-Fortezza cert, only advertise non-Fortezza
716
capabilities. (We advertise all capabilities if we have a
718
if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA))
720
capIndex--; /* we want to visit the same caps index entry next time */
725
* Convert the next preference found in smime_prefs into an
729
cap = smime_capabilities[capIndex];
731
cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability));
734
smime_capabilities[capIndex] = cap;
735
} else if (cap->cipher == smime_prefs[i]) {
736
continue; /* no change to this one */
739
cap->capIDTag = map->algtag;
740
oiddata = SECOID_FindOIDByTag (map->algtag);
744
if (cap->capabilityID.data != NULL) {
745
SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
746
cap->capabilityID.data = NULL;
747
cap->capabilityID.len = 0;
750
rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid));
751
if (rv != SECSuccess)
754
if (map->parms == NULL) {
755
cap->parameters.data = NULL;
756
cap->parameters.len = 0;
758
cap->parameters.data = map->parms->data;
759
cap->parameters.len = map->parms->len;
762
cap->cipher = smime_prefs[i];
765
if (i != smime_current_pref_index)
768
while (capIndex < smime_symmetric_count) {
769
cap = smime_capabilities[capIndex];
771
SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
774
smime_capabilities[capIndex] = NULL;
777
smime_capabilities[capIndex] = NULL;
779
smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
780
smime_capabilities_template);
781
if (smime_encoded_caps == NULL)
784
lastUsedFortezza = isFortezza;
791
smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
793
PRBool isFortezza = PR_FALSE;
795
PORT_Assert (smime_prefs_complete);
796
if (! smime_prefs_complete)
799
/* See if the sender's cert specifies Fortezza key exchange. */
801
isFortezza = PK11_FortezzaHasKEA(cert);
803
/* For that matter, if capabilities haven't been initialized yet,
805
if (isFortezza != lastUsedFortezza || smime_encoded_caps == NULL || smime_prefs_changed) {
808
rv = smime_init_caps(isFortezza);
809
if (rv != SECSuccess)
812
PORT_Assert (smime_encoded_caps != NULL);
815
return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
821
* Start an S/MIME signing context.
823
* "scert" is the cert that will be used to sign the data. It will be
824
* checked for validity.
826
* "ecert" is the signer's encryption cert. If it is different from
827
* scert, then it will be included in the signed message so that the
828
* recipient can save it for future encryptions.
830
* "certdb" is the cert database to use for verifying the cert.
831
* It can be NULL if a default database is available (like in the client).
833
* "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
834
* XXX There should be SECMIME functions for hashing, or the hashing should
835
* be built into this interface, which we would like because we would
836
* support more smartcards that way, and then this argument should go away.)
838
* "digest" is the actual digest of the data. It must be provided in
839
* the case of detached data or NULL if the content will be included.
841
* This function already does all of the stuff specific to S/MIME protocol
842
* and local policy; the return value just needs to be passed to
843
* SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
844
* and finally to SEC_PKCS7DestroyContentInfo().
846
* An error results in a return value of NULL and an error set.
847
* (Retrieve specific errors via PORT_GetError()/XP_GetError().)
850
SEC_PKCS7ContentInfo *
851
SECMIME_CreateSigned (CERTCertificate *scert,
852
CERTCertificate *ecert,
853
CERTCertDBHandle *certdb,
856
SECKEYGetPasswordKey pwfn,
859
SEC_PKCS7ContentInfo *cinfo;
862
/* See note in header comment above about digestalg. */
863
/* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */
865
cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner,
866
certdb, digestalg, digest,
871
if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) {
872
SEC_PKCS7DestroyContentInfo (cinfo);
876
/* if the encryption cert and the signing cert differ, then include
877
* the encryption cert too.
879
/* it is ok to compare the pointers since we ref count, and the same
880
* cert will always have the same pointer
882
if ( ( ecert != NULL ) && ( ecert != scert ) ) {
883
rv = SEC_PKCS7AddCertificate(cinfo, ecert);
884
if ( rv != SECSuccess ) {
885
SEC_PKCS7DestroyContentInfo (cinfo);
890
* Add the signing time. But if it fails for some reason,
891
* may as well not give up altogether -- just assert.
893
rv = SEC_PKCS7AddSigningTime (cinfo);
894
PORT_Assert (rv == SECSuccess);
897
* Add the email profile. Again, if it fails for some reason,
898
* may as well not give up altogether -- just assert.
900
rv = smime_add_profile (ecert, cinfo);
901
PORT_Assert (rv == SECSuccess);