PageRenderTime 81ms CodeModel.GetById 15ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/lib/pkcs7/p7create.c

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