Back to index

lightning-sunbird  0.9+nobinonly
p7create.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * PKCS7 creation.
00039  *
00040  * $Id: p7create.c,v 1.6 2004/04/25 15:03:13 gerv%gerv.net Exp $
00041  */
00042 
00043 #include "p7local.h"
00044 
00045 #include "cert.h"
00046 #include "secasn1.h"
00047 #include "secitem.h"
00048 #include "secoid.h"
00049 #include "pk11func.h"
00050 #include "prtime.h"
00051 #include "secerr.h"
00052 #include "secder.h"
00053 
00054 static SECStatus
00055 sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp,
00056                           SECOidTag kind, PRBool detached)
00057 {
00058     void *thing;
00059     int version;
00060     SECItem *versionp;
00061     SECStatus rv;
00062 
00063     PORT_Assert (cinfo != NULL && poolp != NULL);
00064     if (cinfo == NULL || poolp == NULL)
00065        return SECFailure;
00066 
00067     cinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
00068     PORT_Assert (cinfo->contentTypeTag
00069                && cinfo->contentTypeTag->offset == kind);
00070 
00071     rv = SECITEM_CopyItem (poolp, &(cinfo->contentType),
00072                         &(cinfo->contentTypeTag->oid));
00073     if (rv != SECSuccess)
00074        return rv;
00075 
00076     if (detached)
00077        return SECSuccess;
00078 
00079     switch (kind) {
00080       default:
00081       case SEC_OID_PKCS7_DATA:
00082        thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem));
00083        cinfo->content.data = (SECItem*)thing;
00084        versionp = NULL;
00085        version = -1;
00086        break;
00087       case SEC_OID_PKCS7_DIGESTED_DATA:
00088        thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData));
00089        cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing;
00090        versionp = &(cinfo->content.digestedData->version);
00091        version = SEC_PKCS7_DIGESTED_DATA_VERSION;
00092        break;
00093       case SEC_OID_PKCS7_ENCRYPTED_DATA:
00094        thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData));
00095        cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing;
00096        versionp = &(cinfo->content.encryptedData->version);
00097        version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
00098        break;
00099       case SEC_OID_PKCS7_ENVELOPED_DATA:
00100        thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData));
00101        cinfo->content.envelopedData = 
00102          (SEC_PKCS7EnvelopedData*)thing;
00103        versionp = &(cinfo->content.envelopedData->version);
00104        version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
00105        break;
00106       case SEC_OID_PKCS7_SIGNED_DATA:
00107        thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData));
00108        cinfo->content.signedData = 
00109          (SEC_PKCS7SignedData*)thing;
00110        versionp = &(cinfo->content.signedData->version);
00111        version = SEC_PKCS7_SIGNED_DATA_VERSION;
00112        break;
00113       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00114        thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData));
00115        cinfo->content.signedAndEnvelopedData = 
00116          (SEC_PKCS7SignedAndEnvelopedData*)thing;
00117        versionp = &(cinfo->content.signedAndEnvelopedData->version);
00118        version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
00119        break;
00120     }
00121 
00122     if (thing == NULL)
00123        return SECFailure;
00124 
00125     if (versionp != NULL) {
00126        SECItem *dummy;
00127 
00128        PORT_Assert (version >= 0);
00129        dummy = SEC_ASN1EncodeInteger (poolp, versionp, version);
00130        if (dummy == NULL)
00131            return SECFailure;
00132        PORT_Assert (dummy == versionp);
00133     }
00134 
00135     return SECSuccess;
00136 }
00137 
00138 
00139 static SEC_PKCS7ContentInfo *
00140 sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached,
00141                             SECKEYGetPasswordKey pwfn, void *pwfn_arg)
00142 {
00143     SEC_PKCS7ContentInfo *cinfo;
00144     PRArenaPool *poolp;
00145     SECStatus rv;
00146 
00147     poolp = PORT_NewArena (1024);  /* XXX what is right value? */
00148     if (poolp == NULL)
00149        return NULL;
00150 
00151     cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
00152     if (cinfo == NULL) {
00153        PORT_FreeArena (poolp, PR_FALSE);
00154        return NULL;
00155     }
00156 
00157     cinfo->poolp = poolp;
00158     cinfo->pwfn = pwfn;
00159     cinfo->pwfn_arg = pwfn_arg;
00160     cinfo->created = PR_TRUE;
00161     cinfo->refCount = 1;
00162 
00163     rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached);
00164     if (rv != SECSuccess) {
00165        PORT_FreeArena (poolp, PR_FALSE);
00166        return NULL;
00167     }
00168 
00169     return cinfo;
00170 }
00171 
00172 
00173 /*
00174  * Add a signer to a PKCS7 thing, verifying the signature cert first.
00175  * Any error returns SECFailure.
00176  *
00177  * XXX Right now this only adds the *first* signer.  It fails if you try
00178  * to add a second one -- this needs to be fixed.
00179  */
00180 static SECStatus
00181 sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo,
00182                     CERTCertificate *     cert,
00183                     SECCertUsage          certusage,
00184                     CERTCertDBHandle *    certdb,
00185                     SECOidTag             digestalgtag,
00186                     SECItem *             digestdata)
00187 {
00188     SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
00189     SECAlgorithmID      *digestalg,  **digestalgs,  ***digestalgsp;
00190     SECItem             *digest,     **digests,     ***digestsp;
00191     SECItem *            dummy;
00192     void *               mark;
00193     SECStatus            rv;
00194     SECOidTag            kind;
00195 
00196     kind = SEC_PKCS7ContentType (cinfo);
00197     switch (kind) {
00198       case SEC_OID_PKCS7_SIGNED_DATA:
00199        {
00200            SEC_PKCS7SignedData *sdp;
00201 
00202            sdp = cinfo->content.signedData;
00203            digestalgsp = &(sdp->digestAlgorithms);
00204            digestsp = &(sdp->digests);
00205            signerinfosp = &(sdp->signerInfos);
00206        }
00207        break;
00208       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00209        {
00210            SEC_PKCS7SignedAndEnvelopedData *saedp;
00211 
00212            saedp = cinfo->content.signedAndEnvelopedData;
00213            digestalgsp = &(saedp->digestAlgorithms);
00214            digestsp = &(saedp->digests);
00215            signerinfosp = &(saedp->signerInfos);
00216        }
00217        break;
00218       default:
00219        return SECFailure;          /* XXX set an error? */
00220     }
00221 
00222     /*
00223      * XXX I think that CERT_VerifyCert should do this if *it* is passed
00224      * a NULL database.
00225      */
00226     if (certdb == NULL) {
00227        certdb = CERT_GetDefaultCertDB();
00228        if (certdb == NULL)
00229            return SECFailure;             /* XXX set an error? */
00230     }
00231 
00232     if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
00233                       cinfo->pwfn_arg, NULL) != SECSuccess)
00234        {
00235        /* XXX Did CERT_VerifyCert set an error? */
00236        return SECFailure;
00237     }
00238 
00239     /*
00240      * XXX This is the check that we do not already have a signer.
00241      * This is not what we really want -- we want to allow this
00242      * and *add* the new signer.
00243      */
00244     PORT_Assert (*signerinfosp == NULL
00245                && *digestalgsp == NULL && *digestsp == NULL);
00246     if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
00247        return SECFailure;
00248 
00249     mark = PORT_ArenaMark (cinfo->poolp);
00250 
00251     signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, 
00252                                             sizeof(SEC_PKCS7SignerInfo));
00253     if (signerinfo == NULL) {
00254        PORT_ArenaRelease (cinfo->poolp, mark);
00255        return SECFailure;
00256     }
00257 
00258     dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version,
00259                                SEC_PKCS7_SIGNER_INFO_VERSION);
00260     if (dummy == NULL) {
00261        PORT_ArenaRelease (cinfo->poolp, mark);
00262        return SECFailure;
00263     }
00264     PORT_Assert (dummy == &signerinfo->version);
00265 
00266     signerinfo->cert = CERT_DupCertificate (cert);
00267     if (signerinfo->cert == NULL) {
00268        PORT_ArenaRelease (cinfo->poolp, mark);
00269        return SECFailure;
00270     }
00271 
00272     signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
00273     if (signerinfo->issuerAndSN == NULL) {
00274        PORT_ArenaRelease (cinfo->poolp, mark);
00275        return SECFailure;
00276     }
00277 
00278     rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg,
00279                             digestalgtag, NULL);
00280     if (rv != SECSuccess) {
00281        PORT_ArenaRelease (cinfo->poolp, mark);
00282        return SECFailure;
00283     }
00284 
00285     /*
00286      * Okay, now signerinfo is all set.  We just need to put it and its
00287      * companions (another copy of the digest algorithm, and the digest
00288      * itself if given) into the main structure.
00289      *
00290      * XXX If we are handling more than one signer, the following code
00291      * needs to look through the digest algorithms already specified
00292      * and see if the same one is there already.  If it is, it does not
00293      * need to be added again.  Also, if it is there *and* the digest
00294      * is not null, then the digest given should match the digest already
00295      * specified -- if not, that is an error.  Finally, the new signerinfo
00296      * should be *added* to the set already found.
00297      */
00298 
00299     signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp,
00300                                2 * sizeof(SEC_PKCS7SignerInfo *));
00301     if (signerinfos == NULL) {
00302        PORT_ArenaRelease (cinfo->poolp, mark);
00303        return SECFailure;
00304     }
00305     signerinfos[0] = signerinfo;
00306     signerinfos[1] = NULL;
00307 
00308     digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID));
00309     digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
00310     if (digestalg == NULL || digestalgs == NULL) {
00311        PORT_ArenaRelease (cinfo->poolp, mark);
00312        return SECFailure;
00313     }
00314     rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL);
00315     if (rv != SECSuccess) {
00316        PORT_ArenaRelease (cinfo->poolp, mark);
00317        return SECFailure;
00318     }
00319     digestalgs[0] = digestalg;
00320     digestalgs[1] = NULL;
00321 
00322     if (digestdata != NULL) {
00323        digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem));
00324        digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, 
00325                                          2 * sizeof(SECItem *));
00326        if (digest == NULL || digests == NULL) {
00327            PORT_ArenaRelease (cinfo->poolp, mark);
00328            return SECFailure;
00329        }
00330        rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata);
00331        if (rv != SECSuccess) {
00332            PORT_ArenaRelease (cinfo->poolp, mark);
00333            return SECFailure;
00334        }
00335        digests[0] = digest;
00336        digests[1] = NULL;
00337     } else {
00338        digests = NULL;
00339     }
00340 
00341     *signerinfosp = signerinfos;
00342     *digestalgsp = digestalgs;
00343     *digestsp = digests;
00344 
00345     PORT_ArenaUnmark(cinfo->poolp, mark);
00346     return SECSuccess;
00347 }
00348 
00349 
00350 /*
00351  * Helper function for creating an empty signedData.
00352  */
00353 static SEC_PKCS7ContentInfo *
00354 sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg)
00355 {
00356     SEC_PKCS7ContentInfo *cinfo;
00357     SEC_PKCS7SignedData *sigd;
00358     SECStatus rv;
00359 
00360     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
00361                                       pwfn, pwfn_arg);
00362     if (cinfo == NULL)
00363        return NULL;
00364 
00365     sigd = cinfo->content.signedData;
00366     PORT_Assert (sigd != NULL);
00367 
00368     /*
00369      * XXX Might we want to allow content types other than data?
00370      * If so, via what interface?
00371      */
00372     rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp,
00373                                   SEC_OID_PKCS7_DATA, PR_TRUE);
00374     if (rv != SECSuccess) {
00375        SEC_PKCS7DestroyContentInfo (cinfo);
00376        return NULL;
00377     }
00378 
00379     return cinfo;
00380 }
00381 
00382 
00383 /*
00384  * Start a PKCS7 signing context.
00385  *
00386  * "cert" is the cert that will be used to sign the data.  It will be
00387  * checked for validity.
00388  *
00389  * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
00390  * XXX Maybe SECCertUsage should be split so that our caller just says
00391  * "email" and *we* add the "signing" part -- otherwise our caller
00392  * could be lying about the usage; we do not want to allow encryption
00393  * certs for signing or vice versa.
00394  *
00395  * "certdb" is the cert database to use for verifying the cert.
00396  * It can be NULL if a default database is available (like in the client).
00397  * 
00398  * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
00399  *
00400  * "digest" is the actual digest of the data.  It must be provided in
00401  * the case of detached data or NULL if the content will be included.
00402  *
00403  * The return value can be passed to functions which add things to
00404  * it like attributes, then eventually to SEC_PKCS7Encode() or to
00405  * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
00406  * SEC_PKCS7DestroyContentInfo().
00407  *
00408  * An error results in a return value of NULL and an error set.
00409  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
00410  */
00411 SEC_PKCS7ContentInfo *
00412 SEC_PKCS7CreateSignedData (CERTCertificate *cert,
00413                         SECCertUsage certusage,
00414                         CERTCertDBHandle *certdb,
00415                         SECOidTag digestalg,
00416                         SECItem *digest,
00417                         SECKEYGetPasswordKey pwfn, void *pwfn_arg)
00418 {
00419     SEC_PKCS7ContentInfo *cinfo;
00420     SECStatus rv;
00421 
00422     cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg);
00423     if (cinfo == NULL)
00424        return NULL;
00425 
00426     rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb,
00427                             digestalg, digest);
00428     if (rv != SECSuccess) {
00429        SEC_PKCS7DestroyContentInfo (cinfo);
00430        return NULL;
00431     }
00432 
00433     return cinfo;
00434 }
00435 
00436 
00437 static SEC_PKCS7Attribute *
00438 sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag,
00439                          SECItem *value, PRBool encoded)
00440 {
00441     SEC_PKCS7Attribute *attr;
00442     SECItem **values;
00443     void *mark;
00444 
00445     PORT_Assert (poolp != NULL);
00446     mark = PORT_ArenaMark (poolp);
00447 
00448     attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, 
00449                                            sizeof(SEC_PKCS7Attribute));
00450     if (attr == NULL)
00451        goto loser;
00452 
00453     attr->typeTag = SECOID_FindOIDByTag (oidtag);
00454     if (attr->typeTag == NULL)
00455        goto loser;
00456 
00457     if (SECITEM_CopyItem (poolp, &(attr->type),
00458                        &(attr->typeTag->oid)) != SECSuccess)
00459        goto loser;
00460 
00461     values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *));
00462     if (values == NULL)
00463        goto loser;
00464 
00465     if (value != NULL) {
00466        SECItem *copy;
00467 
00468        copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
00469        if (copy == NULL)
00470            goto loser;
00471 
00472        if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess)
00473            goto loser;
00474 
00475        value = copy;
00476     }
00477 
00478     values[0] = value;
00479     values[1] = NULL;
00480     attr->values = values;
00481     attr->encoded = encoded;
00482 
00483     PORT_ArenaUnmark (poolp, mark);
00484     return attr;
00485 
00486 loser:
00487     PORT_Assert (mark != NULL);
00488     PORT_ArenaRelease (poolp, mark);
00489     return NULL;
00490 }
00491 
00492 
00493 static SECStatus
00494 sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo,
00495                       SEC_PKCS7Attribute ***attrsp,
00496                       SEC_PKCS7Attribute *attr)
00497 {
00498     SEC_PKCS7Attribute **attrs;
00499     SECItem *ct_value;
00500     void *mark;
00501 
00502     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
00503     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
00504        return SECFailure;
00505 
00506     attrs = *attrsp;
00507     if (attrs != NULL) {
00508        int count;
00509 
00510        /*
00511         * We already have some attributes, and just need to add this
00512         * new one.
00513         */
00514 
00515        /*
00516         * We should already have the *required* attributes, which were
00517         * created/added at the same time the first attribute was added.
00518         */
00519        PORT_Assert (sec_PKCS7FindAttribute (attrs,
00520                                         SEC_OID_PKCS9_CONTENT_TYPE,
00521                                         PR_FALSE) != NULL);
00522        PORT_Assert (sec_PKCS7FindAttribute (attrs,
00523                                         SEC_OID_PKCS9_MESSAGE_DIGEST,
00524                                         PR_FALSE) != NULL);
00525 
00526        for (count = 0; attrs[count] != NULL; count++)
00527            ;
00528        attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs,
00529                             (count + 1) * sizeof(SEC_PKCS7Attribute *),
00530                             (count + 2) * sizeof(SEC_PKCS7Attribute *));
00531        if (attrs == NULL)
00532            return SECFailure;
00533 
00534        attrs[count] = attr;
00535        attrs[count+1] = NULL;
00536        *attrsp = attrs;
00537 
00538        return SECSuccess;
00539     }
00540 
00541     /*
00542      * This is the first time an attribute is going in.
00543      * We need to create and add the required attributes, and then
00544      * we will also add in the one our caller gave us.
00545      */
00546 
00547     /*
00548      * There are 2 required attributes, plus the one our caller wants
00549      * to add, plus we always end with a NULL one.  Thus, four slots.
00550      */
00551     attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, 
00552                                       4 * sizeof(SEC_PKCS7Attribute *));
00553     if (attrs == NULL)
00554        return SECFailure;
00555 
00556     mark = PORT_ArenaMark (cinfo->poolp);
00557 
00558     /*
00559      * First required attribute is the content type of the data
00560      * being signed.
00561      */
00562     ct_value = &(cinfo->content.signedData->contentInfo.contentType);
00563     attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp,
00564                                       SEC_OID_PKCS9_CONTENT_TYPE,
00565                                       ct_value, PR_FALSE);
00566     /*
00567      * Second required attribute is the message digest of the data
00568      * being signed; we leave the value NULL for now (just create
00569      * the place for it to go), and the encoder will fill it in later.
00570      */
00571     attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp,
00572                                       SEC_OID_PKCS9_MESSAGE_DIGEST,
00573                                       NULL, PR_FALSE);
00574     if (attrs[0] == NULL || attrs[1] == NULL) {
00575        PORT_ArenaRelease (cinfo->poolp, mark);
00576        return SECFailure; 
00577     }
00578 
00579     attrs[2] = attr;
00580     attrs[3] = NULL;
00581     *attrsp = attrs;
00582 
00583     PORT_ArenaUnmark (cinfo->poolp, mark);
00584     return SECSuccess;
00585 }
00586 
00587 
00588 /*
00589  * Add the signing time to the authenticated (i.e. signed) attributes
00590  * of "cinfo".  This is expected to be included in outgoing signed
00591  * messages for email (S/MIME) but is likely useful in other situations.
00592  *
00593  * This should only be added once; a second call will either do
00594  * nothing or replace an old signing time with a newer one.
00595  *
00596  * XXX This will probably just shove the current time into "cinfo"
00597  * but it will not actually get signed until the entire item is
00598  * processed for encoding.  Is this (expected to be small) delay okay?
00599  *
00600  * "cinfo" should be of type signedData (the only kind of pkcs7 data
00601  * that is allowed authenticated attributes); SECFailure will be returned
00602  * if it is not.
00603  */
00604 SECStatus
00605 SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo)
00606 {
00607     SEC_PKCS7SignerInfo **signerinfos;
00608     SEC_PKCS7Attribute *attr;
00609     SECItem stime;
00610     SECStatus rv;
00611     int si;
00612 
00613     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
00614     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
00615        return SECFailure;
00616 
00617     signerinfos = cinfo->content.signedData->signerInfos;
00618 
00619     /* There has to be a signer, or it makes no sense. */
00620     if (signerinfos == NULL || signerinfos[0] == NULL)
00621        return SECFailure;
00622 
00623     rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
00624     if (rv != SECSuccess)
00625        return rv;
00626 
00627     attr = sec_pkcs7_create_attribute (cinfo->poolp,
00628                                    SEC_OID_PKCS9_SIGNING_TIME,
00629                                    &stime, PR_FALSE);
00630     SECITEM_FreeItem (&stime, PR_FALSE);
00631 
00632     if (attr == NULL)
00633        return SECFailure;
00634 
00635     rv = SECSuccess;
00636     for (si = 0; signerinfos[si] != NULL; si++) {
00637        SEC_PKCS7Attribute *oattr;
00638 
00639        oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr,
00640                                    SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
00641        PORT_Assert (oattr == NULL);
00642        if (oattr != NULL)
00643            continue; /* XXX or would it be better to replace it? */
00644 
00645        rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr),
00646                                   attr);
00647        if (rv != SECSuccess)
00648            break;    /* could try to continue, but may as well give up now */
00649     }
00650 
00651     return rv;
00652 }
00653 
00654 
00655 /*
00656  * Add the specified attribute to the authenticated (i.e. signed) attributes
00657  * of "cinfo" -- "oidtag" describes the attribute and "value" is the
00658  * value to be associated with it.  NOTE! "value" must already be encoded;
00659  * no interpretation of "oidtag" is done.  Also, it is assumed that this
00660  * signedData has only one signer -- if we ever need to add attributes
00661  * when there is more than one signature, we need a way to specify *which*
00662  * signature should get the attribute.
00663  *
00664  * XXX Technically, a signed attribute can have multiple values; if/when
00665  * we ever need to support an attribute which takes multiple values, we
00666  * either need to change this interface or create an AddSignedAttributeValue
00667  * which can be called subsequently, and would then append a value.
00668  *
00669  * "cinfo" should be of type signedData (the only kind of pkcs7 data
00670  * that is allowed authenticated attributes); SECFailure will be returned
00671  * if it is not.
00672  */
00673 SECStatus
00674 SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
00675                           SECOidTag oidtag,
00676                           SECItem *value)
00677 {
00678     SEC_PKCS7SignerInfo **signerinfos;
00679     SEC_PKCS7Attribute *attr;
00680 
00681     PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
00682     if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
00683        return SECFailure;
00684 
00685     signerinfos = cinfo->content.signedData->signerInfos;
00686 
00687     /*
00688      * No signature or more than one means no deal.
00689      */
00690     if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
00691        return SECFailure;
00692 
00693     attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE);
00694     if (attr == NULL)
00695        return SECFailure;
00696 
00697     return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr);
00698 }
00699  
00700 
00701 /*
00702  * Mark that the signer certificates and their issuing chain should
00703  * be included in the encoded data.  This is expected to be used
00704  * in outgoing signed messages for email (S/MIME).
00705  *
00706  * "certdb" is the cert database to use for finding the chain.
00707  * It can be NULL, meaning use the default database.
00708  *
00709  * "cinfo" should be of type signedData or signedAndEnvelopedData;
00710  * SECFailure will be returned if it is not.
00711  */
00712 SECStatus
00713 SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
00714                         CERTCertDBHandle *certdb)
00715 {
00716     SECOidTag kind;
00717     SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
00718 
00719     kind = SEC_PKCS7ContentType (cinfo);
00720     switch (kind) {
00721       case SEC_OID_PKCS7_SIGNED_DATA:
00722        signerinfos = cinfo->content.signedData->signerInfos;
00723        break;
00724       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00725        signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
00726        break;
00727       default:
00728        return SECFailure;          /* XXX set an error? */
00729     }
00730 
00731     if (signerinfos == NULL)              /* no signer, no certs? */
00732        return SECFailure;          /* XXX set an error? */
00733 
00734     if (certdb == NULL) {
00735        certdb = CERT_GetDefaultCertDB();
00736        if (certdb == NULL) {
00737            PORT_SetError (SEC_ERROR_BAD_DATABASE);
00738            return SECFailure;
00739        }
00740     }
00741 
00742     /* XXX Should it be an error if we find no signerinfo or no certs? */
00743     while ((signerinfo = *signerinfos++) != NULL) {
00744        if (signerinfo->cert != NULL)
00745            /* get the cert chain.  don't send the root to avoid contamination
00746             * of old clients with a new root that they don't trust
00747             */
00748            signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert,
00749                                                     certUsageEmailSigner,
00750                                                     PR_FALSE);
00751     }
00752 
00753     return SECSuccess;
00754 }
00755 
00756 
00757 /*
00758  * Helper function to add a certificate chain for inclusion in the
00759  * bag of certificates in a signedData.
00760  */
00761 static SECStatus
00762 sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo,
00763                        CERTCertificate *cert,
00764                        CERTCertDBHandle *certdb)
00765 {
00766     SECOidTag kind;
00767     CERTCertificateList *certlist, **certlists, ***certlistsp;
00768     int count;
00769 
00770     kind = SEC_PKCS7ContentType (cinfo);
00771     switch (kind) {
00772       case SEC_OID_PKCS7_SIGNED_DATA:
00773        {
00774            SEC_PKCS7SignedData *sdp;
00775 
00776            sdp = cinfo->content.signedData;
00777            certlistsp = &(sdp->certLists);
00778        }
00779        break;
00780       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00781        {
00782            SEC_PKCS7SignedAndEnvelopedData *saedp;
00783 
00784            saedp = cinfo->content.signedAndEnvelopedData;
00785            certlistsp = &(saedp->certLists);
00786        }
00787        break;
00788       default:
00789        return SECFailure;          /* XXX set an error? */
00790     }
00791 
00792     if (certdb == NULL) {
00793        certdb = CERT_GetDefaultCertDB();
00794        if (certdb == NULL) {
00795            PORT_SetError (SEC_ERROR_BAD_DATABASE);
00796            return SECFailure;
00797        }
00798     }
00799 
00800     certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE);
00801     if (certlist == NULL)
00802        return SECFailure;
00803 
00804     certlists = *certlistsp;
00805     if (certlists == NULL) {
00806        count = 0;
00807        certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp,
00808                                  2 * sizeof(CERTCertificateList *));
00809     } else {
00810        for (count = 0; certlists[count] != NULL; count++)
00811            ;
00812        PORT_Assert (count); /* should be at least one already */
00813        certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, 
00814                              certlists,
00815                             (count + 1) * sizeof(CERTCertificateList *),
00816                             (count + 2) * sizeof(CERTCertificateList *));
00817     }
00818 
00819     if (certlists == NULL) {
00820        CERT_DestroyCertificateList (certlist);
00821        return SECFailure;
00822     }
00823 
00824     certlists[count] = certlist;
00825     certlists[count + 1] = NULL;
00826 
00827     *certlistsp = certlists;
00828 
00829     return SECSuccess;
00830 }
00831 
00832 
00833 /*
00834  * Helper function to add a certificate for inclusion in the bag of
00835  * certificates in a signedData.
00836  */
00837 static SECStatus
00838 sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo,
00839                         CERTCertificate *cert)
00840 {
00841     SECOidTag kind;
00842     CERTCertificate **certs, ***certsp;
00843     int count;
00844 
00845     kind = SEC_PKCS7ContentType (cinfo);
00846     switch (kind) {
00847       case SEC_OID_PKCS7_SIGNED_DATA:
00848        {
00849            SEC_PKCS7SignedData *sdp;
00850 
00851            sdp = cinfo->content.signedData;
00852            certsp = &(sdp->certs);
00853        }
00854        break;
00855       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
00856        {
00857            SEC_PKCS7SignedAndEnvelopedData *saedp;
00858 
00859            saedp = cinfo->content.signedAndEnvelopedData;
00860            certsp = &(saedp->certs);
00861        }
00862        break;
00863       default:
00864        return SECFailure;          /* XXX set an error? */
00865     }
00866 
00867     cert = CERT_DupCertificate (cert);
00868     if (cert == NULL)
00869        return SECFailure;
00870 
00871     certs = *certsp;
00872     if (certs == NULL) {
00873        count = 0;
00874        certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, 
00875                                          2 * sizeof(CERTCertificate *));
00876     } else {
00877        for (count = 0; certs[count] != NULL; count++)
00878            ;
00879        PORT_Assert (count); /* should be at least one already */
00880        certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs,
00881                             (count + 1) * sizeof(CERTCertificate *),
00882                             (count + 2) * sizeof(CERTCertificate *));
00883     }
00884 
00885     if (certs == NULL) {
00886        CERT_DestroyCertificate (cert);
00887        return SECFailure;
00888     }
00889 
00890     certs[count] = cert;
00891     certs[count + 1] = NULL;
00892 
00893     *certsp = certs;
00894 
00895     return SECSuccess;
00896 }
00897 
00898 
00899 /*
00900  * Create a PKCS7 certs-only container.
00901  *
00902  * "cert" is the (first) cert that will be included.
00903  *
00904  * "include_chain" specifies whether the entire chain for "cert" should
00905  * be included.
00906  *
00907  * "certdb" is the cert database to use for finding the chain.
00908  * It can be NULL in when "include_chain" is false, or when meaning
00909  * use the default database.
00910  *
00911  * More certs and chains can be added via AddCertificate and AddCertChain.
00912  *
00913  * An error results in a return value of NULL and an error set.
00914  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
00915  */
00916 SEC_PKCS7ContentInfo *
00917 SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
00918                        PRBool include_chain,
00919                        CERTCertDBHandle *certdb)
00920 {
00921     SEC_PKCS7ContentInfo *cinfo;
00922     SECStatus rv;
00923 
00924     cinfo = sec_pkcs7_create_signed_data (NULL, NULL);
00925     if (cinfo == NULL)
00926        return NULL;
00927 
00928     if (include_chain)
00929        rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
00930     else
00931        rv = sec_pkcs7_add_certificate (cinfo, cert);
00932 
00933     if (rv != SECSuccess) {
00934        SEC_PKCS7DestroyContentInfo (cinfo);
00935        return NULL;
00936     }
00937 
00938     return cinfo;
00939 }
00940 
00941 
00942 /*
00943  * Add "cert" and its entire chain to the set of certs included in "cinfo".
00944  *
00945  * "certdb" is the cert database to use for finding the chain.
00946  * It can be NULL, meaning use the default database.
00947  *
00948  * "cinfo" should be of type signedData or signedAndEnvelopedData;
00949  * SECFailure will be returned if it is not.
00950  */
00951 SECStatus
00952 SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
00953                      CERTCertificate *cert,
00954                      CERTCertDBHandle *certdb)
00955 {
00956     SECOidTag kind;
00957 
00958     kind = SEC_PKCS7ContentType (cinfo);
00959     if (kind != SEC_OID_PKCS7_SIGNED_DATA
00960        && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
00961        return SECFailure;          /* XXX set an error? */
00962 
00963     return sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
00964 }
00965 
00966 
00967 /*
00968  * Add "cert" to the set of certs included in "cinfo".
00969  *
00970  * "cinfo" should be of type signedData or signedAndEnvelopedData;
00971  * SECFailure will be returned if it is not.
00972  */
00973 SECStatus
00974 SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
00975 {
00976     SECOidTag kind;
00977 
00978     kind = SEC_PKCS7ContentType (cinfo);
00979     if (kind != SEC_OID_PKCS7_SIGNED_DATA
00980        && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
00981        return SECFailure;          /* XXX set an error? */
00982 
00983     return sec_pkcs7_add_certificate (cinfo, cert);
00984 }
00985 
00986 
00987 static SECStatus
00988 sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo,
00989                                    PRArenaPool *poolp,
00990                                    SECOidTag kind, PRBool detached,
00991                                    SECOidTag encalg, int keysize)
00992 {
00993     SECStatus rv;
00994 
00995     PORT_Assert (enccinfo != NULL && poolp != NULL);
00996     if (enccinfo == NULL || poolp == NULL)
00997        return SECFailure;
00998 
00999     /*
01000      * XXX Some day we may want to allow for other kinds.  That needs
01001      * more work and modifications to the creation interface, etc.
01002      * For now, allow but notice callers who pass in other kinds.
01003      * They are responsible for creating the inner type and encoding,
01004      * if it is other than DATA.
01005      */
01006     PORT_Assert (kind == SEC_OID_PKCS7_DATA);
01007 
01008     enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
01009     PORT_Assert (enccinfo->contentTypeTag
01010               && enccinfo->contentTypeTag->offset == kind);
01011 
01012     rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType),
01013                         &(enccinfo->contentTypeTag->oid));
01014     if (rv != SECSuccess)
01015        return rv;
01016 
01017     /* Save keysize and algorithm for later. */
01018     enccinfo->keysize = keysize;
01019     enccinfo->encalg = encalg;
01020 
01021     return SECSuccess;
01022 }
01023 
01024 
01025 /*
01026  * Add a recipient to a PKCS7 thing, verifying their cert first.
01027  * Any error returns SECFailure.
01028  */
01029 static SECStatus
01030 sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo,
01031                       CERTCertificate *cert,
01032                       SECCertUsage certusage,
01033                       CERTCertDBHandle *certdb)
01034 {
01035     SECOidTag kind;
01036     SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
01037     SECItem *dummy;
01038     void *mark;
01039     int count;
01040 
01041     kind = SEC_PKCS7ContentType (cinfo);
01042     switch (kind) {
01043       case SEC_OID_PKCS7_ENVELOPED_DATA:
01044        {
01045            SEC_PKCS7EnvelopedData *edp;
01046 
01047            edp = cinfo->content.envelopedData;
01048            recipientinfosp = &(edp->recipientInfos);
01049        }
01050        break;
01051       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
01052        {
01053            SEC_PKCS7SignedAndEnvelopedData *saedp;
01054 
01055            saedp = cinfo->content.signedAndEnvelopedData;
01056            recipientinfosp = &(saedp->recipientInfos);
01057        }
01058        break;
01059       default:
01060        return SECFailure;          /* XXX set an error? */
01061     }
01062 
01063     /*
01064      * XXX I think that CERT_VerifyCert should do this if *it* is passed
01065      * a NULL database.
01066      */
01067     if (certdb == NULL) {
01068        certdb = CERT_GetDefaultCertDB();
01069        if (certdb == NULL)
01070            return SECFailure;             /* XXX set an error? */
01071     }
01072 
01073     if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
01074                       cinfo->pwfn_arg, NULL) != SECSuccess)
01075        {
01076        /* XXX Did CERT_VerifyCert set an error? */
01077        return SECFailure;
01078     }
01079 
01080     mark = PORT_ArenaMark (cinfo->poolp);
01081 
01082     recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp,
01083                                   sizeof(SEC_PKCS7RecipientInfo));
01084     if (recipientinfo == NULL) {
01085        PORT_ArenaRelease (cinfo->poolp, mark);
01086        return SECFailure;
01087     }
01088 
01089     dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version,
01090                                SEC_PKCS7_RECIPIENT_INFO_VERSION);
01091     if (dummy == NULL) {
01092        PORT_ArenaRelease (cinfo->poolp, mark);
01093        return SECFailure;
01094     }
01095     PORT_Assert (dummy == &recipientinfo->version);
01096 
01097     recipientinfo->cert = CERT_DupCertificate (cert);
01098     if (recipientinfo->cert == NULL) {
01099        PORT_ArenaRelease (cinfo->poolp, mark);
01100        return SECFailure;
01101     }
01102 
01103     recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
01104     if (recipientinfo->issuerAndSN == NULL) {
01105        PORT_ArenaRelease (cinfo->poolp, mark);
01106        return SECFailure;
01107     }
01108 
01109     /*
01110      * Okay, now recipientinfo is all set.  We just need to put it into
01111      * the main structure.
01112      *
01113      * If this is the first recipient, allocate a new recipientinfos array;
01114      * otherwise, reallocate the array, making room for the new entry.
01115      */
01116     recipientinfos = *recipientinfosp;
01117     if (recipientinfos == NULL) {
01118        count = 0;
01119        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc (
01120                                      cinfo->poolp,
01121                                      2 * sizeof(SEC_PKCS7RecipientInfo *));
01122     } else {
01123        for (count = 0; recipientinfos[count] != NULL; count++)
01124            ;
01125        PORT_Assert (count); /* should be at least one already */
01126        recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow (
01127                              cinfo->poolp, recipientinfos,
01128                             (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
01129                             (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
01130     }
01131 
01132     if (recipientinfos == NULL) {
01133        PORT_ArenaRelease (cinfo->poolp, mark);
01134        return SECFailure;
01135     }
01136 
01137     recipientinfos[count] = recipientinfo;
01138     recipientinfos[count + 1] = NULL;
01139 
01140     *recipientinfosp = recipientinfos;
01141 
01142     PORT_ArenaUnmark (cinfo->poolp, mark);
01143     return SECSuccess;
01144 }
01145 
01146 
01147 /*
01148  * Start a PKCS7 enveloping context.
01149  *
01150  * "cert" is the cert for the recipient.  It will be checked for validity.
01151  *
01152  * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
01153  * XXX Maybe SECCertUsage should be split so that our caller just says
01154  * "email" and *we* add the "recipient" part -- otherwise our caller
01155  * could be lying about the usage; we do not want to allow encryption
01156  * certs for signing or vice versa.
01157  *
01158  * "certdb" is the cert database to use for verifying the cert.
01159  * It can be NULL if a default database is available (like in the client).
01160  *
01161  * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
01162  *
01163  * "keysize" specifies the bulk encryption key size, in bits.
01164  *
01165  * The return value can be passed to functions which add things to
01166  * it like more recipients, then eventually to SEC_PKCS7Encode() or to
01167  * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
01168  * SEC_PKCS7DestroyContentInfo().
01169  *
01170  * An error results in a return value of NULL and an error set.
01171  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
01172  */
01173 extern SEC_PKCS7ContentInfo *
01174 SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
01175                            SECCertUsage certusage,
01176                            CERTCertDBHandle *certdb,
01177                            SECOidTag encalg,
01178                            int keysize,
01179                            SECKEYGetPasswordKey pwfn, void *pwfn_arg)
01180 {
01181     SEC_PKCS7ContentInfo *cinfo;
01182     SEC_PKCS7EnvelopedData *envd;
01183     SECStatus rv;
01184 
01185     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA,
01186                                       PR_FALSE, pwfn, pwfn_arg);
01187     if (cinfo == NULL)
01188        return NULL;
01189 
01190     rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
01191     if (rv != SECSuccess) {
01192        SEC_PKCS7DestroyContentInfo (cinfo);
01193        return NULL;
01194     }
01195 
01196     envd = cinfo->content.envelopedData;
01197     PORT_Assert (envd != NULL);
01198 
01199     /*
01200      * XXX Might we want to allow content types other than data?
01201      * If so, via what interface?
01202      */
01203     rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo),
01204                                           cinfo->poolp,
01205                                           SEC_OID_PKCS7_DATA, PR_FALSE,
01206                                           encalg, keysize);
01207     if (rv != SECSuccess) {
01208        SEC_PKCS7DestroyContentInfo (cinfo);
01209        return NULL;
01210     }
01211 
01212     /* XXX Anything more to do here? */
01213 
01214     return cinfo;
01215 }
01216 
01217 
01218 /*
01219  * Add another recipient to an encrypted message.
01220  *
01221  * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
01222  * SECFailure will be returned if it is not.
01223  *
01224  * "cert" is the cert for the recipient.  It will be checked for validity.
01225  *
01226  * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
01227  * XXX Maybe SECCertUsage should be split so that our caller just says
01228  * "email" and *we* add the "recipient" part -- otherwise our caller
01229  * could be lying about the usage; we do not want to allow encryption
01230  * certs for signing or vice versa.
01231  *
01232  * "certdb" is the cert database to use for verifying the cert.
01233  * It can be NULL if a default database is available (like in the client).
01234  */
01235 SECStatus
01236 SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
01237                      CERTCertificate *cert,
01238                      SECCertUsage certusage,
01239                      CERTCertDBHandle *certdb)
01240 {
01241     return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
01242 }
01243 
01244 
01245 /*
01246  * Create an empty PKCS7 data content info.
01247  *
01248  * An error results in a return value of NULL and an error set.
01249  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
01250  */
01251 SEC_PKCS7ContentInfo *
01252 SEC_PKCS7CreateData (void)
01253 {
01254     return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE,
01255                                      NULL, NULL);
01256 }
01257 
01258 
01259 /*
01260  * Create an empty PKCS7 encrypted content info.
01261  *
01262  * "algorithm" specifies the bulk encryption algorithm to use.
01263  * 
01264  * An error results in a return value of NULL and an error set.
01265  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
01266  */
01267 SEC_PKCS7ContentInfo *
01268 SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
01269                            SECKEYGetPasswordKey pwfn, void *pwfn_arg)
01270 {
01271     SEC_PKCS7ContentInfo *cinfo;
01272     SECAlgorithmID *algid;
01273     SEC_PKCS7EncryptedData *enc_data;
01274     SECStatus rv;
01275 
01276     cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, 
01277                                       PR_FALSE, pwfn, pwfn_arg);
01278     if (cinfo == NULL)
01279        return NULL;
01280 
01281     enc_data = cinfo->content.encryptedData;
01282     algid = &(enc_data->encContentInfo.contentEncAlg);
01283 
01284     switch (algorithm) {
01285       case SEC_OID_RC2_CBC:
01286       case SEC_OID_DES_EDE3_CBC:
01287       case SEC_OID_DES_CBC:
01288        rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL);
01289        break;
01290       default:
01291        {
01292            /*
01293             * Assume password-based-encryption.  At least, try that.
01294             */
01295            SECAlgorithmID *pbe_algid;
01296            pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL);
01297            if (pbe_algid == NULL) {
01298               rv = SECFailure;
01299            } else {
01300               rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid);
01301               SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
01302            }
01303        }
01304        break;
01305     }
01306 
01307     if (rv != SECSuccess) {
01308        SEC_PKCS7DestroyContentInfo (cinfo);
01309        return NULL;
01310     }
01311 
01312     rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo),
01313                                           cinfo->poolp,
01314                                           SEC_OID_PKCS7_DATA, PR_FALSE,
01315                                           algorithm, keysize);
01316     if (rv != SECSuccess) {
01317        SEC_PKCS7DestroyContentInfo (cinfo);
01318        return NULL;
01319     }
01320 
01321     return cinfo;
01322 }
01323