security/nss/lib/pkcs7/p7create.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial