Imported Upstream version 3.13.6
[platform/upstream/nss.git] / mozilla / security / nss / lib / pkcs7 / p7create.c
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
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/
8  *
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
12  * License.
13  *
14  * The Original Code is the Netscape security libraries.
15  *
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.
20  *
21  * Contributor(s):
22  *
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.
34  *
35  * ***** END LICENSE BLOCK ***** */
36
37 /*
38  * PKCS7 creation.
39  *
40  * $Id: p7create.c,v 1.10 2012/03/19 22:16:34 kaie%kuix.de Exp $
41  */
42
43 #include "p7local.h"
44
45 #include "cert.h"
46 #include "secasn1.h"
47 #include "secitem.h"
48 #include "secoid.h"
49 #include "pk11func.h"
50 #include "prtime.h"
51 #include "secerr.h"
52 #include "secder.h"
53 #include "secpkcs5.h"
54
55 const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */
56
57 static SECStatus
58 sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp,
59                              SECOidTag kind, PRBool detached)
60 {
61     void *thing;
62     int version;
63     SECItem *versionp;
64     SECStatus rv;
65
66     PORT_Assert (cinfo != NULL && poolp != NULL);
67     if (cinfo == NULL || poolp == NULL)
68         return SECFailure;
69
70     cinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
71     PORT_Assert (cinfo->contentTypeTag
72                  && cinfo->contentTypeTag->offset == kind);
73
74     rv = SECITEM_CopyItem (poolp, &(cinfo->contentType),
75                            &(cinfo->contentTypeTag->oid));
76     if (rv != SECSuccess)
77         return rv;
78
79     if (detached)
80         return SECSuccess;
81
82     switch (kind) {
83       default:
84       case SEC_OID_PKCS7_DATA:
85         thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem));
86         cinfo->content.data = (SECItem*)thing;
87         versionp = NULL;
88         version = -1;
89         break;
90       case SEC_OID_PKCS7_DIGESTED_DATA:
91         thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData));
92         cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing;
93         versionp = &(cinfo->content.digestedData->version);
94         version = SEC_PKCS7_DIGESTED_DATA_VERSION;
95         break;
96       case SEC_OID_PKCS7_ENCRYPTED_DATA:
97         thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData));
98         cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing;
99         versionp = &(cinfo->content.encryptedData->version);
100         version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
101         break;
102       case SEC_OID_PKCS7_ENVELOPED_DATA:
103         thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData));
104         cinfo->content.envelopedData = 
105           (SEC_PKCS7EnvelopedData*)thing;
106         versionp = &(cinfo->content.envelopedData->version);
107         version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
108         break;
109       case SEC_OID_PKCS7_SIGNED_DATA:
110         thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData));
111         cinfo->content.signedData = 
112           (SEC_PKCS7SignedData*)thing;
113         versionp = &(cinfo->content.signedData->version);
114         version = SEC_PKCS7_SIGNED_DATA_VERSION;
115         break;
116       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
117         thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData));
118         cinfo->content.signedAndEnvelopedData = 
119           (SEC_PKCS7SignedAndEnvelopedData*)thing;
120         versionp = &(cinfo->content.signedAndEnvelopedData->version);
121         version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
122         break;
123     }
124
125     if (thing == NULL)
126         return SECFailure;
127
128     if (versionp != NULL) {
129         SECItem *dummy;
130
131         PORT_Assert (version >= 0);
132         dummy = SEC_ASN1EncodeInteger (poolp, versionp, version);
133         if (dummy == NULL)
134             return SECFailure;
135         PORT_Assert (dummy == versionp);
136     }
137
138     return SECSuccess;
139 }
140
141
142 static SEC_PKCS7ContentInfo *
143 sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached,
144                                SECKEYGetPasswordKey pwfn, void *pwfn_arg)
145 {
146     SEC_PKCS7ContentInfo *cinfo;
147     PRArenaPool *poolp;
148     SECStatus rv;
149
150     poolp = PORT_NewArena (1024);       /* XXX what is right value? */
151     if (poolp == NULL)
152         return NULL;
153
154     cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
155     if (cinfo == NULL) {
156         PORT_FreeArena (poolp, PR_FALSE);
157         return NULL;
158     }
159
160     cinfo->poolp = poolp;
161     cinfo->pwfn = pwfn;
162     cinfo->pwfn_arg = pwfn_arg;
163     cinfo->created = PR_TRUE;
164     cinfo->refCount = 1;
165
166     rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached);
167     if (rv != SECSuccess) {
168         PORT_FreeArena (poolp, PR_FALSE);
169         return NULL;
170     }
171
172     return cinfo;
173 }
174
175
176 /*
177  * Add a signer to a PKCS7 thing, verifying the signature cert first.
178  * Any error returns SECFailure.
179  *
180  * XXX Right now this only adds the *first* signer.  It fails if you try
181  * to add a second one -- this needs to be fixed.
182  */
183 static SECStatus
184 sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo,
185                       CERTCertificate *     cert,
186                       SECCertUsage          certusage,
187                       CERTCertDBHandle *    certdb,
188                       SECOidTag             digestalgtag,
189                       SECItem *             digestdata)
190 {
191     SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
192     SECAlgorithmID      *digestalg,  **digestalgs,  ***digestalgsp;
193     SECItem             *digest,     **digests,     ***digestsp;
194     SECItem *            dummy;
195     void *               mark;
196     SECStatus            rv;
197     SECOidTag            kind;
198
199     kind = SEC_PKCS7ContentType (cinfo);
200     switch (kind) {
201       case SEC_OID_PKCS7_SIGNED_DATA:
202         {
203             SEC_PKCS7SignedData *sdp;
204
205             sdp = cinfo->content.signedData;
206             digestalgsp = &(sdp->digestAlgorithms);
207             digestsp = &(sdp->digests);
208             signerinfosp = &(sdp->signerInfos);
209         }
210         break;
211       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
212         {
213             SEC_PKCS7SignedAndEnvelopedData *saedp;
214
215             saedp = cinfo->content.signedAndEnvelopedData;
216             digestalgsp = &(saedp->digestAlgorithms);
217             digestsp = &(saedp->digests);
218             signerinfosp = &(saedp->signerInfos);
219         }
220         break;
221       default:
222         return SECFailure;              /* XXX set an error? */
223     }
224
225     /*
226      * XXX I think that CERT_VerifyCert should do this if *it* is passed
227      * a NULL database.
228      */
229     if (certdb == NULL) {
230         certdb = CERT_GetDefaultCertDB();
231         if (certdb == NULL)
232             return SECFailure;          /* XXX set an error? */
233     }
234
235     if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
236                          cinfo->pwfn_arg, NULL) != SECSuccess)
237         {
238         /* XXX Did CERT_VerifyCert set an error? */
239         return SECFailure;
240     }
241
242     /*
243      * XXX This is the check that we do not already have a signer.
244      * This is not what we really want -- we want to allow this
245      * and *add* the new signer.
246      */
247     PORT_Assert (*signerinfosp == NULL
248                  && *digestalgsp == NULL && *digestsp == NULL);
249     if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
250         return SECFailure;
251
252     mark = PORT_ArenaMark (cinfo->poolp);
253
254     signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, 
255                                                   sizeof(SEC_PKCS7SignerInfo));
256     if (signerinfo == NULL) {
257         PORT_ArenaRelease (cinfo->poolp, mark);
258         return SECFailure;
259     }
260
261     dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version,
262                                    SEC_PKCS7_SIGNER_INFO_VERSION);
263     if (dummy == NULL) {
264         PORT_ArenaRelease (cinfo->poolp, mark);
265         return SECFailure;
266     }
267     PORT_Assert (dummy == &signerinfo->version);
268
269     signerinfo->cert = CERT_DupCertificate (cert);
270     if (signerinfo->cert == NULL) {
271         PORT_ArenaRelease (cinfo->poolp, mark);
272         return SECFailure;
273     }
274
275     signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
276     if (signerinfo->issuerAndSN == NULL) {
277         PORT_ArenaRelease (cinfo->poolp, mark);
278         return SECFailure;
279     }
280
281     rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg,
282                                 digestalgtag, NULL);
283     if (rv != SECSuccess) {
284         PORT_ArenaRelease (cinfo->poolp, mark);
285         return SECFailure;
286     }
287
288     /*
289      * Okay, now signerinfo is all set.  We just need to put it and its
290      * companions (another copy of the digest algorithm, and the digest
291      * itself if given) into the main structure.
292      *
293      * XXX If we are handling more than one signer, the following code
294      * needs to look through the digest algorithms already specified
295      * and see if the same one is there already.  If it is, it does not
296      * need to be added again.  Also, if it is there *and* the digest
297      * is not null, then the digest given should match the digest already
298      * specified -- if not, that is an error.  Finally, the new signerinfo
299      * should be *added* to the set already found.
300      */
301
302     signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp,
303                                    2 * sizeof(SEC_PKCS7SignerInfo *));
304     if (signerinfos == NULL) {
305         PORT_ArenaRelease (cinfo->poolp, mark);
306         return SECFailure;
307     }
308     signerinfos[0] = signerinfo;
309     signerinfos[1] = NULL;
310
311     digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID));
312     digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
313     if (digestalg == NULL || digestalgs == NULL) {
314         PORT_ArenaRelease (cinfo->poolp, mark);
315         return SECFailure;
316     }
317     rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL);
318     if (rv != SECSuccess) {
319         PORT_ArenaRelease (cinfo->poolp, mark);
320         return SECFailure;
321     }
322     digestalgs[0] = digestalg;
323     digestalgs[1] = NULL;
324
325     if (digestdata != NULL) {
326         digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem));
327         digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, 
328                                               2 * sizeof(SECItem *));
329         if (digest == NULL || digests == NULL) {
330             PORT_ArenaRelease (cinfo->poolp, mark);
331             return SECFailure;
332         }
333         rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata);
334         if (rv != SECSuccess) {
335             PORT_ArenaRelease (cinfo->poolp, mark);
336             return SECFailure;
337         }
338         digests[0] = digest;
339         digests[1] = NULL;
340     } else {
341         digests = NULL;
342     }
343
344     *signerinfosp = signerinfos;
345     *digestalgsp = digestalgs;
346     *digestsp = digests;
347
348     PORT_ArenaUnmark(cinfo->poolp, mark);
349     return SECSuccess;
350 }
351
352
353 /*
354  * Helper function for creating an empty signedData.
355  */
356 static SEC_PKCS7ContentInfo *
357 sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg)
358 {
359     SEC_PKCS7ContentInfo *cinfo;
360     SEC_PKCS7SignedData *sigd;
361     SECStatus rv;
362
363     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
364                                            pwfn, pwfn_arg);
365     if (cinfo == NULL)
366         return NULL;
367
368     sigd = cinfo->content.signedData;
369     PORT_Assert (sigd != NULL);
370
371     /*
372      * XXX Might we want to allow content types other than data?
373      * If so, via what interface?
374      */
375     rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp,
376                                       SEC_OID_PKCS7_DATA, PR_TRUE);
377     if (rv != SECSuccess) {
378         SEC_PKCS7DestroyContentInfo (cinfo);
379         return NULL;
380     }
381
382     return cinfo;
383 }
384
385
386 /*
387  * Start a PKCS7 signing context.
388  *
389  * "cert" is the cert that will be used to sign the data.  It will be
390  * checked for validity.
391  *
392  * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
393  * XXX Maybe SECCertUsage should be split so that our caller just says
394  * "email" and *we* add the "signing" part -- otherwise our caller
395  * could be lying about the usage; we do not want to allow encryption
396  * certs for signing or vice versa.
397  *
398  * "certdb" is the cert database to use for verifying the cert.
399  * It can be NULL if a default database is available (like in the client).
400  * 
401  * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
402  *
403  * "digest" is the actual digest of the data.  It must be provided in
404  * the case of detached data or NULL if the content will be included.
405  *
406  * The return value can be passed to functions which add things to
407  * it like attributes, then eventually to SEC_PKCS7Encode() or to
408  * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
409  * SEC_PKCS7DestroyContentInfo().
410  *
411  * An error results in a return value of NULL and an error set.
412  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
413  */
414 SEC_PKCS7ContentInfo *
415 SEC_PKCS7CreateSignedData (CERTCertificate *cert,
416                            SECCertUsage certusage,
417                            CERTCertDBHandle *certdb,
418                            SECOidTag digestalg,
419                            SECItem *digest,
420                            SECKEYGetPasswordKey pwfn, void *pwfn_arg)
421 {
422     SEC_PKCS7ContentInfo *cinfo;
423     SECStatus rv;
424
425     cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg);
426     if (cinfo == NULL)
427         return NULL;
428
429     rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb,
430                                digestalg, digest);
431     if (rv != SECSuccess) {
432         SEC_PKCS7DestroyContentInfo (cinfo);
433         return NULL;
434     }
435
436     return cinfo;
437 }
438
439
440 static SEC_PKCS7Attribute *
441 sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag,
442                             SECItem *value, PRBool encoded)
443 {
444     SEC_PKCS7Attribute *attr;
445     SECItem **values;
446     void *mark;
447
448     PORT_Assert (poolp != NULL);
449     mark = PORT_ArenaMark (poolp);
450
451     attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, 
452                                                  sizeof(SEC_PKCS7Attribute));
453     if (attr == NULL)
454         goto loser;
455
456     attr->typeTag = SECOID_FindOIDByTag (oidtag);
457     if (attr->typeTag == NULL)
458         goto loser;
459
460     if (SECITEM_CopyItem (poolp, &(attr->type),
461                           &(attr->typeTag->oid)) != SECSuccess)
462         goto loser;
463
464     values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *));
465     if (values == NULL)
466         goto loser;
467
468     if (value != NULL) {
469         SECItem *copy;
470
471         copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
472         if (copy == NULL)
473             goto loser;
474
475         if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess)
476             goto loser;
477
478         value = copy;
479     }
480
481     values[0] = value;
482     values[1] = NULL;
483     attr->values = values;
484     attr->encoded = encoded;
485
486     PORT_ArenaUnmark (poolp, mark);
487     return attr;
488
489 loser:
490     PORT_Assert (mark != NULL);
491     PORT_ArenaRelease (poolp, mark);
492     return NULL;
493 }
494
495
496 static SECStatus
497 sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo,
498                          SEC_PKCS7Attribute ***attrsp,
499                          SEC_PKCS7Attribute *attr)
500 {
501     SEC_PKCS7Attribute **attrs;
502     SECItem *ct_value;
503     void *mark;
504
505     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
506     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
507         return SECFailure;
508
509     attrs = *attrsp;
510     if (attrs != NULL) {
511         int count;
512
513         /*
514          * We already have some attributes, and just need to add this
515          * new one.
516          */
517
518         /*
519          * We should already have the *required* attributes, which were
520          * created/added at the same time the first attribute was added.
521          */
522         PORT_Assert (sec_PKCS7FindAttribute (attrs,
523                                              SEC_OID_PKCS9_CONTENT_TYPE,
524                                              PR_FALSE) != NULL);
525         PORT_Assert (sec_PKCS7FindAttribute (attrs,
526                                              SEC_OID_PKCS9_MESSAGE_DIGEST,
527                                              PR_FALSE) != NULL);
528
529         for (count = 0; attrs[count] != NULL; count++)
530             ;
531         attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs,
532                                 (count + 1) * sizeof(SEC_PKCS7Attribute *),
533                                 (count + 2) * sizeof(SEC_PKCS7Attribute *));
534         if (attrs == NULL)
535             return SECFailure;
536
537         attrs[count] = attr;
538         attrs[count+1] = NULL;
539         *attrsp = attrs;
540
541         return SECSuccess;
542     }
543
544     /*
545      * This is the first time an attribute is going in.
546      * We need to create and add the required attributes, and then
547      * we will also add in the one our caller gave us.
548      */
549
550     /*
551      * There are 2 required attributes, plus the one our caller wants
552      * to add, plus we always end with a NULL one.  Thus, four slots.
553      */
554     attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, 
555                                            4 * sizeof(SEC_PKCS7Attribute *));
556     if (attrs == NULL)
557         return SECFailure;
558
559     mark = PORT_ArenaMark (cinfo->poolp);
560
561     /*
562      * First required attribute is the content type of the data
563      * being signed.
564      */
565     ct_value = &(cinfo->content.signedData->contentInfo.contentType);
566     attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp,
567                                            SEC_OID_PKCS9_CONTENT_TYPE,
568                                            ct_value, PR_FALSE);
569     /*
570      * Second required attribute is the message digest of the data
571      * being signed; we leave the value NULL for now (just create
572      * the place for it to go), and the encoder will fill it in later.
573      */
574     attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp,
575                                            SEC_OID_PKCS9_MESSAGE_DIGEST,
576                                            NULL, PR_FALSE);
577     if (attrs[0] == NULL || attrs[1] == NULL) {
578         PORT_ArenaRelease (cinfo->poolp, mark);
579         return SECFailure; 
580     }
581
582     attrs[2] = attr;
583     attrs[3] = NULL;
584     *attrsp = attrs;
585
586     PORT_ArenaUnmark (cinfo->poolp, mark);
587     return SECSuccess;
588 }
589
590
591 /*
592  * Add the signing time to the authenticated (i.e. signed) attributes
593  * of "cinfo".  This is expected to be included in outgoing signed
594  * messages for email (S/MIME) but is likely useful in other situations.
595  *
596  * This should only be added once; a second call will either do
597  * nothing or replace an old signing time with a newer one.
598  *
599  * XXX This will probably just shove the current time into "cinfo"
600  * but it will not actually get signed until the entire item is
601  * processed for encoding.  Is this (expected to be small) delay okay?
602  *
603  * "cinfo" should be of type signedData (the only kind of pkcs7 data
604  * that is allowed authenticated attributes); SECFailure will be returned
605  * if it is not.
606  */
607 SECStatus
608 SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo)
609 {
610     SEC_PKCS7SignerInfo **signerinfos;
611     SEC_PKCS7Attribute *attr;
612     SECItem stime;
613     SECStatus rv;
614     int si;
615
616     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
617     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
618         return SECFailure;
619
620     signerinfos = cinfo->content.signedData->signerInfos;
621
622     /* There has to be a signer, or it makes no sense. */
623     if (signerinfos == NULL || signerinfos[0] == NULL)
624         return SECFailure;
625
626     rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
627     if (rv != SECSuccess)
628         return rv;
629
630     attr = sec_pkcs7_create_attribute (cinfo->poolp,
631                                        SEC_OID_PKCS9_SIGNING_TIME,
632                                        &stime, PR_FALSE);
633     SECITEM_FreeItem (&stime, PR_FALSE);
634
635     if (attr == NULL)
636         return SECFailure;
637
638     rv = SECSuccess;
639     for (si = 0; signerinfos[si] != NULL; si++) {
640         SEC_PKCS7Attribute *oattr;
641
642         oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr,
643                                         SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
644         PORT_Assert (oattr == NULL);
645         if (oattr != NULL)
646             continue;   /* XXX or would it be better to replace it? */
647
648         rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr),
649                                       attr);
650         if (rv != SECSuccess)
651             break;      /* could try to continue, but may as well give up now */
652     }
653
654     return rv;
655 }
656
657
658 /*
659  * Add the specified attribute to the authenticated (i.e. signed) attributes
660  * of "cinfo" -- "oidtag" describes the attribute and "value" is the
661  * value to be associated with it.  NOTE! "value" must already be encoded;
662  * no interpretation of "oidtag" is done.  Also, it is assumed that this
663  * signedData has only one signer -- if we ever need to add attributes
664  * when there is more than one signature, we need a way to specify *which*
665  * signature should get the attribute.
666  *
667  * XXX Technically, a signed attribute can have multiple values; if/when
668  * we ever need to support an attribute which takes multiple values, we
669  * either need to change this interface or create an AddSignedAttributeValue
670  * which can be called subsequently, and would then append a value.
671  *
672  * "cinfo" should be of type signedData (the only kind of pkcs7 data
673  * that is allowed authenticated attributes); SECFailure will be returned
674  * if it is not.
675  */
676 SECStatus
677 SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
678                              SECOidTag oidtag,
679                              SECItem *value)
680 {
681     SEC_PKCS7SignerInfo **signerinfos;
682     SEC_PKCS7Attribute *attr;
683
684     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
685     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
686         return SECFailure;
687
688     signerinfos = cinfo->content.signedData->signerInfos;
689
690     /*
691      * No signature or more than one means no deal.
692      */
693     if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
694         return SECFailure;
695
696     attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE);
697     if (attr == NULL)
698         return SECFailure;
699
700     return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr);
701 }
702  
703
704 /*
705  * Mark that the signer certificates and their issuing chain should
706  * be included in the encoded data.  This is expected to be used
707  * in outgoing signed messages for email (S/MIME).
708  *
709  * "certdb" is the cert database to use for finding the chain.
710  * It can be NULL, meaning use the default database.
711  *
712  * "cinfo" should be of type signedData or signedAndEnvelopedData;
713  * SECFailure will be returned if it is not.
714  */
715 SECStatus
716 SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
717                            CERTCertDBHandle *certdb)
718 {
719     SECOidTag kind;
720     SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
721
722     kind = SEC_PKCS7ContentType (cinfo);
723     switch (kind) {
724       case SEC_OID_PKCS7_SIGNED_DATA:
725         signerinfos = cinfo->content.signedData->signerInfos;
726         break;
727       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
728         signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
729         break;
730       default:
731         return SECFailure;              /* XXX set an error? */
732     }
733
734     if (signerinfos == NULL)            /* no signer, no certs? */
735         return SECFailure;              /* XXX set an error? */
736
737     if (certdb == NULL) {
738         certdb = CERT_GetDefaultCertDB();
739         if (certdb == NULL) {
740             PORT_SetError (SEC_ERROR_BAD_DATABASE);
741             return SECFailure;
742         }
743     }
744
745     /* XXX Should it be an error if we find no signerinfo or no certs? */
746     while ((signerinfo = *signerinfos++) != NULL) {
747         if (signerinfo->cert != NULL)
748             /* get the cert chain.  don't send the root to avoid contamination
749              * of old clients with a new root that they don't trust
750              */
751             signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert,
752                                                            certUsageEmailSigner,
753                                                            PR_FALSE);
754     }
755
756     return SECSuccess;
757 }
758
759
760 /*
761  * Helper function to add a certificate chain for inclusion in the
762  * bag of certificates in a signedData.
763  */
764 static SECStatus
765 sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo,
766                           CERTCertificate *cert,
767                           CERTCertDBHandle *certdb)
768 {
769     SECOidTag kind;
770     CERTCertificateList *certlist, **certlists, ***certlistsp;
771     int count;
772
773     kind = SEC_PKCS7ContentType (cinfo);
774     switch (kind) {
775       case SEC_OID_PKCS7_SIGNED_DATA:
776         {
777             SEC_PKCS7SignedData *sdp;
778
779             sdp = cinfo->content.signedData;
780             certlistsp = &(sdp->certLists);
781         }
782         break;
783       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
784         {
785             SEC_PKCS7SignedAndEnvelopedData *saedp;
786
787             saedp = cinfo->content.signedAndEnvelopedData;
788             certlistsp = &(saedp->certLists);
789         }
790         break;
791       default:
792         return SECFailure;              /* XXX set an error? */
793     }
794
795     if (certdb == NULL) {
796         certdb = CERT_GetDefaultCertDB();
797         if (certdb == NULL) {
798             PORT_SetError (SEC_ERROR_BAD_DATABASE);
799             return SECFailure;
800         }
801     }
802
803     certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE);
804     if (certlist == NULL)
805         return SECFailure;
806
807     certlists = *certlistsp;
808     if (certlists == NULL) {
809         count = 0;
810         certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp,
811                                      2 * sizeof(CERTCertificateList *));
812     } else {
813         for (count = 0; certlists[count] != NULL; count++)
814             ;
815         PORT_Assert (count);    /* should be at least one already */
816         certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, 
817                                  certlists,
818                                 (count + 1) * sizeof(CERTCertificateList *),
819                                 (count + 2) * sizeof(CERTCertificateList *));
820     }
821
822     if (certlists == NULL) {
823         CERT_DestroyCertificateList (certlist);
824         return SECFailure;
825     }
826
827     certlists[count] = certlist;
828     certlists[count + 1] = NULL;
829
830     *certlistsp = certlists;
831
832     return SECSuccess;
833 }
834
835
836 /*
837  * Helper function to add a certificate for inclusion in the bag of
838  * certificates in a signedData.
839  */
840 static SECStatus
841 sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo,
842                            CERTCertificate *cert)
843 {
844     SECOidTag kind;
845     CERTCertificate **certs, ***certsp;
846     int count;
847
848     kind = SEC_PKCS7ContentType (cinfo);
849     switch (kind) {
850       case SEC_OID_PKCS7_SIGNED_DATA:
851         {
852             SEC_PKCS7SignedData *sdp;
853
854             sdp = cinfo->content.signedData;
855             certsp = &(sdp->certs);
856         }
857         break;
858       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
859         {
860             SEC_PKCS7SignedAndEnvelopedData *saedp;
861
862             saedp = cinfo->content.signedAndEnvelopedData;
863             certsp = &(saedp->certs);
864         }
865         break;
866       default:
867         return SECFailure;              /* XXX set an error? */
868     }
869
870     cert = CERT_DupCertificate (cert);
871     if (cert == NULL)
872         return SECFailure;
873
874     certs = *certsp;
875     if (certs == NULL) {
876         count = 0;
877         certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, 
878                                               2 * sizeof(CERTCertificate *));
879     } else {
880         for (count = 0; certs[count] != NULL; count++)
881             ;
882         PORT_Assert (count);    /* should be at least one already */
883         certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs,
884                                 (count + 1) * sizeof(CERTCertificate *),
885                                 (count + 2) * sizeof(CERTCertificate *));
886     }
887
888     if (certs == NULL) {
889         CERT_DestroyCertificate (cert);
890         return SECFailure;
891     }
892
893     certs[count] = cert;
894     certs[count + 1] = NULL;
895
896     *certsp = certs;
897
898     return SECSuccess;
899 }
900
901
902 /*
903  * Create a PKCS7 certs-only container.
904  *
905  * "cert" is the (first) cert that will be included.
906  *
907  * "include_chain" specifies whether the entire chain for "cert" should
908  * be included.
909  *
910  * "certdb" is the cert database to use for finding the chain.
911  * It can be NULL in when "include_chain" is false, or when meaning
912  * use the default database.
913  *
914  * More certs and chains can be added via AddCertificate and AddCertChain.
915  *
916  * An error results in a return value of NULL and an error set.
917  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
918  */
919 SEC_PKCS7ContentInfo *
920 SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
921                           PRBool include_chain,
922                           CERTCertDBHandle *certdb)
923 {
924     SEC_PKCS7ContentInfo *cinfo;
925     SECStatus rv;
926
927     cinfo = sec_pkcs7_create_signed_data (NULL, NULL);
928     if (cinfo == NULL)
929         return NULL;
930
931     if (include_chain)
932         rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
933     else
934         rv = sec_pkcs7_add_certificate (cinfo, cert);
935
936     if (rv != SECSuccess) {
937         SEC_PKCS7DestroyContentInfo (cinfo);
938         return NULL;
939     }
940
941     return cinfo;
942 }
943
944
945 /*
946  * Add "cert" and its entire chain to the set of certs included in "cinfo".
947  *
948  * "certdb" is the cert database to use for finding the chain.
949  * It can be NULL, meaning use the default database.
950  *
951  * "cinfo" should be of type signedData or signedAndEnvelopedData;
952  * SECFailure will be returned if it is not.
953  */
954 SECStatus
955 SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
956                        CERTCertificate *cert,
957                        CERTCertDBHandle *certdb)
958 {
959     SECOidTag kind;
960
961     kind = SEC_PKCS7ContentType (cinfo);
962     if (kind != SEC_OID_PKCS7_SIGNED_DATA
963         && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
964         return SECFailure;              /* XXX set an error? */
965
966     return sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
967 }
968
969
970 /*
971  * Add "cert" to the set of certs included in "cinfo".
972  *
973  * "cinfo" should be of type signedData or signedAndEnvelopedData;
974  * SECFailure will be returned if it is not.
975  */
976 SECStatus
977 SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
978 {
979     SECOidTag kind;
980
981     kind = SEC_PKCS7ContentType (cinfo);
982     if (kind != SEC_OID_PKCS7_SIGNED_DATA
983         && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
984         return SECFailure;              /* XXX set an error? */
985
986     return sec_pkcs7_add_certificate (cinfo, cert);
987 }
988
989
990 static SECStatus
991 sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo,
992                                        PRArenaPool *poolp,
993                                        SECOidTag kind, PRBool detached,
994                                        SECOidTag encalg, int keysize)
995 {
996     SECStatus rv;
997
998     PORT_Assert (enccinfo != NULL && poolp != NULL);
999     if (enccinfo == NULL || poolp == NULL)
1000         return SECFailure;
1001
1002     /*
1003      * XXX Some day we may want to allow for other kinds.  That needs
1004      * more work and modifications to the creation interface, etc.
1005      * For now, allow but notice callers who pass in other kinds.
1006      * They are responsible for creating the inner type and encoding,
1007      * if it is other than DATA.
1008      */
1009     PORT_Assert (kind == SEC_OID_PKCS7_DATA);
1010
1011     enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
1012     PORT_Assert (enccinfo->contentTypeTag
1013                && enccinfo->contentTypeTag->offset == kind);
1014
1015     rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType),
1016                            &(enccinfo->contentTypeTag->oid));
1017     if (rv != SECSuccess)
1018         return rv;
1019
1020     /* Save keysize and algorithm for later. */
1021     enccinfo->keysize = keysize;
1022     enccinfo->encalg = encalg;
1023
1024     return SECSuccess;
1025 }
1026
1027
1028 /*
1029  * Add a recipient to a PKCS7 thing, verifying their cert first.
1030  * Any error returns SECFailure.
1031  */
1032 static SECStatus
1033 sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo,
1034                          CERTCertificate *cert,
1035                          SECCertUsage certusage,
1036                          CERTCertDBHandle *certdb)
1037 {
1038     SECOidTag kind;
1039     SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
1040     SECItem *dummy;
1041     void *mark;
1042     int count;
1043
1044     kind = SEC_PKCS7ContentType (cinfo);
1045     switch (kind) {
1046       case SEC_OID_PKCS7_ENVELOPED_DATA:
1047         {
1048             SEC_PKCS7EnvelopedData *edp;
1049
1050             edp = cinfo->content.envelopedData;
1051             recipientinfosp = &(edp->recipientInfos);
1052         }
1053         break;
1054       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
1055         {
1056             SEC_PKCS7SignedAndEnvelopedData *saedp;
1057
1058             saedp = cinfo->content.signedAndEnvelopedData;
1059             recipientinfosp = &(saedp->recipientInfos);
1060         }
1061         break;
1062       default:
1063         return SECFailure;              /* XXX set an error? */
1064     }
1065
1066     /*
1067      * XXX I think that CERT_VerifyCert should do this if *it* is passed
1068      * a NULL database.
1069      */
1070     if (certdb == NULL) {
1071         certdb = CERT_GetDefaultCertDB();
1072         if (certdb == NULL)
1073             return SECFailure;          /* XXX set an error? */
1074     }
1075
1076     if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
1077                          cinfo->pwfn_arg, NULL) != SECSuccess)
1078         {
1079         /* XXX Did CERT_VerifyCert set an error? */
1080         return SECFailure;
1081     }
1082
1083     mark = PORT_ArenaMark (cinfo->poolp);
1084
1085     recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp,
1086                                       sizeof(SEC_PKCS7RecipientInfo));
1087     if (recipientinfo == NULL) {
1088         PORT_ArenaRelease (cinfo->poolp, mark);
1089         return SECFailure;
1090     }
1091
1092     dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version,
1093                                    SEC_PKCS7_RECIPIENT_INFO_VERSION);
1094     if (dummy == NULL) {
1095         PORT_ArenaRelease (cinfo->poolp, mark);
1096         return SECFailure;
1097     }
1098     PORT_Assert (dummy == &recipientinfo->version);
1099
1100     recipientinfo->cert = CERT_DupCertificate (cert);
1101     if (recipientinfo->cert == NULL) {
1102         PORT_ArenaRelease (cinfo->poolp, mark);
1103         return SECFailure;
1104     }
1105
1106     recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
1107     if (recipientinfo->issuerAndSN == NULL) {
1108         PORT_ArenaRelease (cinfo->poolp, mark);
1109         return SECFailure;
1110     }
1111
1112     /*
1113      * Okay, now recipientinfo is all set.  We just need to put it into
1114      * the main structure.
1115      *
1116      * If this is the first recipient, allocate a new recipientinfos array;
1117      * otherwise, reallocate the array, making room for the new entry.
1118      */
1119     recipientinfos = *recipientinfosp;
1120     if (recipientinfos == NULL) {
1121         count = 0;
1122         recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc (
1123                                           cinfo->poolp,
1124                                           2 * sizeof(SEC_PKCS7RecipientInfo *));
1125     } else {
1126         for (count = 0; recipientinfos[count] != NULL; count++)
1127             ;
1128         PORT_Assert (count);    /* should be at least one already */
1129         recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow (
1130                                  cinfo->poolp, recipientinfos,
1131                                 (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
1132                                 (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
1133     }
1134
1135     if (recipientinfos == NULL) {
1136         PORT_ArenaRelease (cinfo->poolp, mark);
1137         return SECFailure;
1138     }
1139
1140     recipientinfos[count] = recipientinfo;
1141     recipientinfos[count + 1] = NULL;
1142
1143     *recipientinfosp = recipientinfos;
1144
1145     PORT_ArenaUnmark (cinfo->poolp, mark);
1146     return SECSuccess;
1147 }
1148
1149
1150 /*
1151  * Start a PKCS7 enveloping context.
1152  *
1153  * "cert" is the cert for the recipient.  It will be checked for validity.
1154  *
1155  * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1156  * XXX Maybe SECCertUsage should be split so that our caller just says
1157  * "email" and *we* add the "recipient" part -- otherwise our caller
1158  * could be lying about the usage; we do not want to allow encryption
1159  * certs for signing or vice versa.
1160  *
1161  * "certdb" is the cert database to use for verifying the cert.
1162  * It can be NULL if a default database is available (like in the client).
1163  *
1164  * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
1165  *
1166  * "keysize" specifies the bulk encryption key size, in bits.
1167  *
1168  * The return value can be passed to functions which add things to
1169  * it like more recipients, then eventually to SEC_PKCS7Encode() or to
1170  * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
1171  * SEC_PKCS7DestroyContentInfo().
1172  *
1173  * An error results in a return value of NULL and an error set.
1174  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1175  */
1176 extern SEC_PKCS7ContentInfo *
1177 SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
1178                               SECCertUsage certusage,
1179                               CERTCertDBHandle *certdb,
1180                               SECOidTag encalg,
1181                               int keysize,
1182                               SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1183 {
1184     SEC_PKCS7ContentInfo *cinfo;
1185     SEC_PKCS7EnvelopedData *envd;
1186     SECStatus rv;
1187
1188     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA,
1189                                            PR_FALSE, pwfn, pwfn_arg);
1190     if (cinfo == NULL)
1191         return NULL;
1192
1193     rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
1194     if (rv != SECSuccess) {
1195         SEC_PKCS7DestroyContentInfo (cinfo);
1196         return NULL;
1197     }
1198
1199     envd = cinfo->content.envelopedData;
1200     PORT_Assert (envd != NULL);
1201
1202     /*
1203      * XXX Might we want to allow content types other than data?
1204      * If so, via what interface?
1205      */
1206     rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo),
1207                                                 cinfo->poolp,
1208                                                 SEC_OID_PKCS7_DATA, PR_FALSE,
1209                                                 encalg, keysize);
1210     if (rv != SECSuccess) {
1211         SEC_PKCS7DestroyContentInfo (cinfo);
1212         return NULL;
1213     }
1214
1215     /* XXX Anything more to do here? */
1216
1217     return cinfo;
1218 }
1219
1220
1221 /*
1222  * Add another recipient to an encrypted message.
1223  *
1224  * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
1225  * SECFailure will be returned if it is not.
1226  *
1227  * "cert" is the cert for the recipient.  It will be checked for validity.
1228  *
1229  * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1230  * XXX Maybe SECCertUsage should be split so that our caller just says
1231  * "email" and *we* add the "recipient" part -- otherwise our caller
1232  * could be lying about the usage; we do not want to allow encryption
1233  * certs for signing or vice versa.
1234  *
1235  * "certdb" is the cert database to use for verifying the cert.
1236  * It can be NULL if a default database is available (like in the client).
1237  */
1238 SECStatus
1239 SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
1240                        CERTCertificate *cert,
1241                        SECCertUsage certusage,
1242                        CERTCertDBHandle *certdb)
1243 {
1244     return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
1245 }
1246
1247
1248 /*
1249  * Create an empty PKCS7 data content info.
1250  *
1251  * An error results in a return value of NULL and an error set.
1252  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1253  */
1254 SEC_PKCS7ContentInfo *
1255 SEC_PKCS7CreateData (void)
1256 {
1257     return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE,
1258                                           NULL, NULL);
1259 }
1260
1261
1262 /*
1263  * Create an empty PKCS7 encrypted content info.
1264  *
1265  * "algorithm" specifies the bulk encryption algorithm to use.
1266  * 
1267  * An error results in a return value of NULL and an error set.
1268  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1269  */
1270 SEC_PKCS7ContentInfo *
1271 SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
1272                               SECKEYGetPasswordKey pwfn, void *pwfn_arg)
1273 {
1274     SEC_PKCS7ContentInfo *cinfo;
1275     SECAlgorithmID *algid;
1276     SEC_PKCS7EncryptedData *enc_data;
1277     SECStatus rv;
1278
1279     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, 
1280                                            PR_FALSE, pwfn, pwfn_arg);
1281     if (cinfo == NULL)
1282         return NULL;
1283
1284     enc_data = cinfo->content.encryptedData;
1285     algid = &(enc_data->encContentInfo.contentEncAlg);
1286
1287     if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
1288         rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL);
1289     } else {
1290         /* Assume password-based-encryption.  
1291          * Note: we can't generate pkcs5v2 from this interface.
1292          * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
1293          * non-PBE oids and assuming that they are pkcs5v2 oids, but
1294          * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
1295          * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
1296          * to create pkcs5v2 PBEs */
1297         SECAlgorithmID *pbe_algid;
1298         pbe_algid = PK11_CreatePBEAlgorithmID(algorithm,
1299                                               NSS_PBE_DEFAULT_ITERATION_COUNT,
1300                                               NULL);
1301         if (pbe_algid == NULL) {
1302             rv = SECFailure;
1303         } else {
1304             rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid);
1305             SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
1306         }
1307     }
1308
1309     if (rv != SECSuccess) {
1310         SEC_PKCS7DestroyContentInfo (cinfo);
1311         return NULL;
1312     }
1313
1314     rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo),
1315                                                 cinfo->poolp,
1316                                                 SEC_OID_PKCS7_DATA, PR_FALSE,
1317                                                 algorithm, keysize);
1318     if (rv != SECSuccess) {
1319         SEC_PKCS7DestroyContentInfo (cinfo);
1320         return NULL;
1321     }
1322
1323     return cinfo;
1324 }
1325