1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pkcs7/p7create.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1291 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * PKCS7 creation. 1.10 + */ 1.11 + 1.12 +#include "p7local.h" 1.13 + 1.14 +#include "cert.h" 1.15 +#include "secasn1.h" 1.16 +#include "secitem.h" 1.17 +#include "secoid.h" 1.18 +#include "pk11func.h" 1.19 +#include "prtime.h" 1.20 +#include "secerr.h" 1.21 +#include "secder.h" 1.22 +#include "secpkcs5.h" 1.23 + 1.24 +const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */ 1.25 + 1.26 +static SECStatus 1.27 +sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp, 1.28 + SECOidTag kind, PRBool detached) 1.29 +{ 1.30 + void *thing; 1.31 + int version; 1.32 + SECItem *versionp; 1.33 + SECStatus rv; 1.34 + 1.35 + PORT_Assert (cinfo != NULL && poolp != NULL); 1.36 + if (cinfo == NULL || poolp == NULL) 1.37 + return SECFailure; 1.38 + 1.39 + cinfo->contentTypeTag = SECOID_FindOIDByTag (kind); 1.40 + PORT_Assert (cinfo->contentTypeTag 1.41 + && cinfo->contentTypeTag->offset == kind); 1.42 + 1.43 + rv = SECITEM_CopyItem (poolp, &(cinfo->contentType), 1.44 + &(cinfo->contentTypeTag->oid)); 1.45 + if (rv != SECSuccess) 1.46 + return rv; 1.47 + 1.48 + if (detached) 1.49 + return SECSuccess; 1.50 + 1.51 + switch (kind) { 1.52 + default: 1.53 + case SEC_OID_PKCS7_DATA: 1.54 + thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem)); 1.55 + cinfo->content.data = (SECItem*)thing; 1.56 + versionp = NULL; 1.57 + version = -1; 1.58 + break; 1.59 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.60 + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData)); 1.61 + cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing; 1.62 + versionp = &(cinfo->content.digestedData->version); 1.63 + version = SEC_PKCS7_DIGESTED_DATA_VERSION; 1.64 + break; 1.65 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.66 + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData)); 1.67 + cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing; 1.68 + versionp = &(cinfo->content.encryptedData->version); 1.69 + version = SEC_PKCS7_ENCRYPTED_DATA_VERSION; 1.70 + break; 1.71 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.72 + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData)); 1.73 + cinfo->content.envelopedData = 1.74 + (SEC_PKCS7EnvelopedData*)thing; 1.75 + versionp = &(cinfo->content.envelopedData->version); 1.76 + version = SEC_PKCS7_ENVELOPED_DATA_VERSION; 1.77 + break; 1.78 + case SEC_OID_PKCS7_SIGNED_DATA: 1.79 + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData)); 1.80 + cinfo->content.signedData = 1.81 + (SEC_PKCS7SignedData*)thing; 1.82 + versionp = &(cinfo->content.signedData->version); 1.83 + version = SEC_PKCS7_SIGNED_DATA_VERSION; 1.84 + break; 1.85 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.86 + thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData)); 1.87 + cinfo->content.signedAndEnvelopedData = 1.88 + (SEC_PKCS7SignedAndEnvelopedData*)thing; 1.89 + versionp = &(cinfo->content.signedAndEnvelopedData->version); 1.90 + version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION; 1.91 + break; 1.92 + } 1.93 + 1.94 + if (thing == NULL) 1.95 + return SECFailure; 1.96 + 1.97 + if (versionp != NULL) { 1.98 + SECItem *dummy; 1.99 + 1.100 + PORT_Assert (version >= 0); 1.101 + dummy = SEC_ASN1EncodeInteger (poolp, versionp, version); 1.102 + if (dummy == NULL) 1.103 + return SECFailure; 1.104 + PORT_Assert (dummy == versionp); 1.105 + } 1.106 + 1.107 + return SECSuccess; 1.108 +} 1.109 + 1.110 + 1.111 +static SEC_PKCS7ContentInfo * 1.112 +sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached, 1.113 + SECKEYGetPasswordKey pwfn, void *pwfn_arg) 1.114 +{ 1.115 + SEC_PKCS7ContentInfo *cinfo; 1.116 + PLArenaPool *poolp; 1.117 + SECStatus rv; 1.118 + 1.119 + poolp = PORT_NewArena (1024); /* XXX what is right value? */ 1.120 + if (poolp == NULL) 1.121 + return NULL; 1.122 + 1.123 + cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); 1.124 + if (cinfo == NULL) { 1.125 + PORT_FreeArena (poolp, PR_FALSE); 1.126 + return NULL; 1.127 + } 1.128 + 1.129 + cinfo->poolp = poolp; 1.130 + cinfo->pwfn = pwfn; 1.131 + cinfo->pwfn_arg = pwfn_arg; 1.132 + cinfo->created = PR_TRUE; 1.133 + cinfo->refCount = 1; 1.134 + 1.135 + rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached); 1.136 + if (rv != SECSuccess) { 1.137 + PORT_FreeArena (poolp, PR_FALSE); 1.138 + return NULL; 1.139 + } 1.140 + 1.141 + return cinfo; 1.142 +} 1.143 + 1.144 + 1.145 +/* 1.146 + * Add a signer to a PKCS7 thing, verifying the signature cert first. 1.147 + * Any error returns SECFailure. 1.148 + * 1.149 + * XXX Right now this only adds the *first* signer. It fails if you try 1.150 + * to add a second one -- this needs to be fixed. 1.151 + */ 1.152 +static SECStatus 1.153 +sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo, 1.154 + CERTCertificate * cert, 1.155 + SECCertUsage certusage, 1.156 + CERTCertDBHandle * certdb, 1.157 + SECOidTag digestalgtag, 1.158 + SECItem * digestdata) 1.159 +{ 1.160 + SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp; 1.161 + SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp; 1.162 + SECItem *digest, **digests, ***digestsp; 1.163 + SECItem * dummy; 1.164 + void * mark; 1.165 + SECStatus rv; 1.166 + SECOidTag kind; 1.167 + 1.168 + kind = SEC_PKCS7ContentType (cinfo); 1.169 + switch (kind) { 1.170 + case SEC_OID_PKCS7_SIGNED_DATA: 1.171 + { 1.172 + SEC_PKCS7SignedData *sdp; 1.173 + 1.174 + sdp = cinfo->content.signedData; 1.175 + digestalgsp = &(sdp->digestAlgorithms); 1.176 + digestsp = &(sdp->digests); 1.177 + signerinfosp = &(sdp->signerInfos); 1.178 + } 1.179 + break; 1.180 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.181 + { 1.182 + SEC_PKCS7SignedAndEnvelopedData *saedp; 1.183 + 1.184 + saedp = cinfo->content.signedAndEnvelopedData; 1.185 + digestalgsp = &(saedp->digestAlgorithms); 1.186 + digestsp = &(saedp->digests); 1.187 + signerinfosp = &(saedp->signerInfos); 1.188 + } 1.189 + break; 1.190 + default: 1.191 + return SECFailure; /* XXX set an error? */ 1.192 + } 1.193 + 1.194 + /* 1.195 + * XXX I think that CERT_VerifyCert should do this if *it* is passed 1.196 + * a NULL database. 1.197 + */ 1.198 + if (certdb == NULL) { 1.199 + certdb = CERT_GetDefaultCertDB(); 1.200 + if (certdb == NULL) 1.201 + return SECFailure; /* XXX set an error? */ 1.202 + } 1.203 + 1.204 + if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), 1.205 + cinfo->pwfn_arg, NULL) != SECSuccess) 1.206 + { 1.207 + /* XXX Did CERT_VerifyCert set an error? */ 1.208 + return SECFailure; 1.209 + } 1.210 + 1.211 + /* 1.212 + * XXX This is the check that we do not already have a signer. 1.213 + * This is not what we really want -- we want to allow this 1.214 + * and *add* the new signer. 1.215 + */ 1.216 + PORT_Assert (*signerinfosp == NULL 1.217 + && *digestalgsp == NULL && *digestsp == NULL); 1.218 + if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL) 1.219 + return SECFailure; 1.220 + 1.221 + mark = PORT_ArenaMark (cinfo->poolp); 1.222 + 1.223 + signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, 1.224 + sizeof(SEC_PKCS7SignerInfo)); 1.225 + if (signerinfo == NULL) { 1.226 + PORT_ArenaRelease (cinfo->poolp, mark); 1.227 + return SECFailure; 1.228 + } 1.229 + 1.230 + dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version, 1.231 + SEC_PKCS7_SIGNER_INFO_VERSION); 1.232 + if (dummy == NULL) { 1.233 + PORT_ArenaRelease (cinfo->poolp, mark); 1.234 + return SECFailure; 1.235 + } 1.236 + PORT_Assert (dummy == &signerinfo->version); 1.237 + 1.238 + signerinfo->cert = CERT_DupCertificate (cert); 1.239 + if (signerinfo->cert == NULL) { 1.240 + PORT_ArenaRelease (cinfo->poolp, mark); 1.241 + return SECFailure; 1.242 + } 1.243 + 1.244 + signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); 1.245 + if (signerinfo->issuerAndSN == NULL) { 1.246 + PORT_ArenaRelease (cinfo->poolp, mark); 1.247 + return SECFailure; 1.248 + } 1.249 + 1.250 + rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg, 1.251 + digestalgtag, NULL); 1.252 + if (rv != SECSuccess) { 1.253 + PORT_ArenaRelease (cinfo->poolp, mark); 1.254 + return SECFailure; 1.255 + } 1.256 + 1.257 + /* 1.258 + * Okay, now signerinfo is all set. We just need to put it and its 1.259 + * companions (another copy of the digest algorithm, and the digest 1.260 + * itself if given) into the main structure. 1.261 + * 1.262 + * XXX If we are handling more than one signer, the following code 1.263 + * needs to look through the digest algorithms already specified 1.264 + * and see if the same one is there already. If it is, it does not 1.265 + * need to be added again. Also, if it is there *and* the digest 1.266 + * is not null, then the digest given should match the digest already 1.267 + * specified -- if not, that is an error. Finally, the new signerinfo 1.268 + * should be *added* to the set already found. 1.269 + */ 1.270 + 1.271 + signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp, 1.272 + 2 * sizeof(SEC_PKCS7SignerInfo *)); 1.273 + if (signerinfos == NULL) { 1.274 + PORT_ArenaRelease (cinfo->poolp, mark); 1.275 + return SECFailure; 1.276 + } 1.277 + signerinfos[0] = signerinfo; 1.278 + signerinfos[1] = NULL; 1.279 + 1.280 + digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID)); 1.281 + digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *)); 1.282 + if (digestalg == NULL || digestalgs == NULL) { 1.283 + PORT_ArenaRelease (cinfo->poolp, mark); 1.284 + return SECFailure; 1.285 + } 1.286 + rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL); 1.287 + if (rv != SECSuccess) { 1.288 + PORT_ArenaRelease (cinfo->poolp, mark); 1.289 + return SECFailure; 1.290 + } 1.291 + digestalgs[0] = digestalg; 1.292 + digestalgs[1] = NULL; 1.293 + 1.294 + if (digestdata != NULL) { 1.295 + digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem)); 1.296 + digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, 1.297 + 2 * sizeof(SECItem *)); 1.298 + if (digest == NULL || digests == NULL) { 1.299 + PORT_ArenaRelease (cinfo->poolp, mark); 1.300 + return SECFailure; 1.301 + } 1.302 + rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata); 1.303 + if (rv != SECSuccess) { 1.304 + PORT_ArenaRelease (cinfo->poolp, mark); 1.305 + return SECFailure; 1.306 + } 1.307 + digests[0] = digest; 1.308 + digests[1] = NULL; 1.309 + } else { 1.310 + digests = NULL; 1.311 + } 1.312 + 1.313 + *signerinfosp = signerinfos; 1.314 + *digestalgsp = digestalgs; 1.315 + *digestsp = digests; 1.316 + 1.317 + PORT_ArenaUnmark(cinfo->poolp, mark); 1.318 + return SECSuccess; 1.319 +} 1.320 + 1.321 + 1.322 +/* 1.323 + * Helper function for creating an empty signedData. 1.324 + */ 1.325 +static SEC_PKCS7ContentInfo * 1.326 +sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg) 1.327 +{ 1.328 + SEC_PKCS7ContentInfo *cinfo; 1.329 + SEC_PKCS7SignedData *sigd; 1.330 + SECStatus rv; 1.331 + 1.332 + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE, 1.333 + pwfn, pwfn_arg); 1.334 + if (cinfo == NULL) 1.335 + return NULL; 1.336 + 1.337 + sigd = cinfo->content.signedData; 1.338 + PORT_Assert (sigd != NULL); 1.339 + 1.340 + /* 1.341 + * XXX Might we want to allow content types other than data? 1.342 + * If so, via what interface? 1.343 + */ 1.344 + rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp, 1.345 + SEC_OID_PKCS7_DATA, PR_TRUE); 1.346 + if (rv != SECSuccess) { 1.347 + SEC_PKCS7DestroyContentInfo (cinfo); 1.348 + return NULL; 1.349 + } 1.350 + 1.351 + return cinfo; 1.352 +} 1.353 + 1.354 + 1.355 +/* 1.356 + * Start a PKCS7 signing context. 1.357 + * 1.358 + * "cert" is the cert that will be used to sign the data. It will be 1.359 + * checked for validity. 1.360 + * 1.361 + * "certusage" describes the signing usage (e.g. certUsageEmailSigner) 1.362 + * XXX Maybe SECCertUsage should be split so that our caller just says 1.363 + * "email" and *we* add the "signing" part -- otherwise our caller 1.364 + * could be lying about the usage; we do not want to allow encryption 1.365 + * certs for signing or vice versa. 1.366 + * 1.367 + * "certdb" is the cert database to use for verifying the cert. 1.368 + * It can be NULL if a default database is available (like in the client). 1.369 + * 1.370 + * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). 1.371 + * 1.372 + * "digest" is the actual digest of the data. It must be provided in 1.373 + * the case of detached data or NULL if the content will be included. 1.374 + * 1.375 + * The return value can be passed to functions which add things to 1.376 + * it like attributes, then eventually to SEC_PKCS7Encode() or to 1.377 + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to 1.378 + * SEC_PKCS7DestroyContentInfo(). 1.379 + * 1.380 + * An error results in a return value of NULL and an error set. 1.381 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.382 + */ 1.383 +SEC_PKCS7ContentInfo * 1.384 +SEC_PKCS7CreateSignedData (CERTCertificate *cert, 1.385 + SECCertUsage certusage, 1.386 + CERTCertDBHandle *certdb, 1.387 + SECOidTag digestalg, 1.388 + SECItem *digest, 1.389 + SECKEYGetPasswordKey pwfn, void *pwfn_arg) 1.390 +{ 1.391 + SEC_PKCS7ContentInfo *cinfo; 1.392 + SECStatus rv; 1.393 + 1.394 + cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg); 1.395 + if (cinfo == NULL) 1.396 + return NULL; 1.397 + 1.398 + rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb, 1.399 + digestalg, digest); 1.400 + if (rv != SECSuccess) { 1.401 + SEC_PKCS7DestroyContentInfo (cinfo); 1.402 + return NULL; 1.403 + } 1.404 + 1.405 + return cinfo; 1.406 +} 1.407 + 1.408 + 1.409 +static SEC_PKCS7Attribute * 1.410 +sec_pkcs7_create_attribute (PLArenaPool *poolp, SECOidTag oidtag, 1.411 + SECItem *value, PRBool encoded) 1.412 +{ 1.413 + SEC_PKCS7Attribute *attr; 1.414 + SECItem **values; 1.415 + void *mark; 1.416 + 1.417 + PORT_Assert (poolp != NULL); 1.418 + mark = PORT_ArenaMark (poolp); 1.419 + 1.420 + attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, 1.421 + sizeof(SEC_PKCS7Attribute)); 1.422 + if (attr == NULL) 1.423 + goto loser; 1.424 + 1.425 + attr->typeTag = SECOID_FindOIDByTag (oidtag); 1.426 + if (attr->typeTag == NULL) 1.427 + goto loser; 1.428 + 1.429 + if (SECITEM_CopyItem (poolp, &(attr->type), 1.430 + &(attr->typeTag->oid)) != SECSuccess) 1.431 + goto loser; 1.432 + 1.433 + values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *)); 1.434 + if (values == NULL) 1.435 + goto loser; 1.436 + 1.437 + if (value != NULL) { 1.438 + SECItem *copy; 1.439 + 1.440 + copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); 1.441 + if (copy == NULL) 1.442 + goto loser; 1.443 + 1.444 + if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess) 1.445 + goto loser; 1.446 + 1.447 + value = copy; 1.448 + } 1.449 + 1.450 + values[0] = value; 1.451 + values[1] = NULL; 1.452 + attr->values = values; 1.453 + attr->encoded = encoded; 1.454 + 1.455 + PORT_ArenaUnmark (poolp, mark); 1.456 + return attr; 1.457 + 1.458 +loser: 1.459 + PORT_Assert (mark != NULL); 1.460 + PORT_ArenaRelease (poolp, mark); 1.461 + return NULL; 1.462 +} 1.463 + 1.464 + 1.465 +static SECStatus 1.466 +sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo, 1.467 + SEC_PKCS7Attribute ***attrsp, 1.468 + SEC_PKCS7Attribute *attr) 1.469 +{ 1.470 + SEC_PKCS7Attribute **attrs; 1.471 + SECItem *ct_value; 1.472 + void *mark; 1.473 + 1.474 + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); 1.475 + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) 1.476 + return SECFailure; 1.477 + 1.478 + attrs = *attrsp; 1.479 + if (attrs != NULL) { 1.480 + int count; 1.481 + 1.482 + /* 1.483 + * We already have some attributes, and just need to add this 1.484 + * new one. 1.485 + */ 1.486 + 1.487 + /* 1.488 + * We should already have the *required* attributes, which were 1.489 + * created/added at the same time the first attribute was added. 1.490 + */ 1.491 + PORT_Assert (sec_PKCS7FindAttribute (attrs, 1.492 + SEC_OID_PKCS9_CONTENT_TYPE, 1.493 + PR_FALSE) != NULL); 1.494 + PORT_Assert (sec_PKCS7FindAttribute (attrs, 1.495 + SEC_OID_PKCS9_MESSAGE_DIGEST, 1.496 + PR_FALSE) != NULL); 1.497 + 1.498 + for (count = 0; attrs[count] != NULL; count++) 1.499 + ; 1.500 + attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs, 1.501 + (count + 1) * sizeof(SEC_PKCS7Attribute *), 1.502 + (count + 2) * sizeof(SEC_PKCS7Attribute *)); 1.503 + if (attrs == NULL) 1.504 + return SECFailure; 1.505 + 1.506 + attrs[count] = attr; 1.507 + attrs[count+1] = NULL; 1.508 + *attrsp = attrs; 1.509 + 1.510 + return SECSuccess; 1.511 + } 1.512 + 1.513 + /* 1.514 + * This is the first time an attribute is going in. 1.515 + * We need to create and add the required attributes, and then 1.516 + * we will also add in the one our caller gave us. 1.517 + */ 1.518 + 1.519 + /* 1.520 + * There are 2 required attributes, plus the one our caller wants 1.521 + * to add, plus we always end with a NULL one. Thus, four slots. 1.522 + */ 1.523 + attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, 1.524 + 4 * sizeof(SEC_PKCS7Attribute *)); 1.525 + if (attrs == NULL) 1.526 + return SECFailure; 1.527 + 1.528 + mark = PORT_ArenaMark (cinfo->poolp); 1.529 + 1.530 + /* 1.531 + * First required attribute is the content type of the data 1.532 + * being signed. 1.533 + */ 1.534 + ct_value = &(cinfo->content.signedData->contentInfo.contentType); 1.535 + attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp, 1.536 + SEC_OID_PKCS9_CONTENT_TYPE, 1.537 + ct_value, PR_FALSE); 1.538 + /* 1.539 + * Second required attribute is the message digest of the data 1.540 + * being signed; we leave the value NULL for now (just create 1.541 + * the place for it to go), and the encoder will fill it in later. 1.542 + */ 1.543 + attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp, 1.544 + SEC_OID_PKCS9_MESSAGE_DIGEST, 1.545 + NULL, PR_FALSE); 1.546 + if (attrs[0] == NULL || attrs[1] == NULL) { 1.547 + PORT_ArenaRelease (cinfo->poolp, mark); 1.548 + return SECFailure; 1.549 + } 1.550 + 1.551 + attrs[2] = attr; 1.552 + attrs[3] = NULL; 1.553 + *attrsp = attrs; 1.554 + 1.555 + PORT_ArenaUnmark (cinfo->poolp, mark); 1.556 + return SECSuccess; 1.557 +} 1.558 + 1.559 + 1.560 +/* 1.561 + * Add the signing time to the authenticated (i.e. signed) attributes 1.562 + * of "cinfo". This is expected to be included in outgoing signed 1.563 + * messages for email (S/MIME) but is likely useful in other situations. 1.564 + * 1.565 + * This should only be added once; a second call will either do 1.566 + * nothing or replace an old signing time with a newer one. 1.567 + * 1.568 + * XXX This will probably just shove the current time into "cinfo" 1.569 + * but it will not actually get signed until the entire item is 1.570 + * processed for encoding. Is this (expected to be small) delay okay? 1.571 + * 1.572 + * "cinfo" should be of type signedData (the only kind of pkcs7 data 1.573 + * that is allowed authenticated attributes); SECFailure will be returned 1.574 + * if it is not. 1.575 + */ 1.576 +SECStatus 1.577 +SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo) 1.578 +{ 1.579 + SEC_PKCS7SignerInfo **signerinfos; 1.580 + SEC_PKCS7Attribute *attr; 1.581 + SECItem stime; 1.582 + SECStatus rv; 1.583 + int si; 1.584 + 1.585 + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); 1.586 + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) 1.587 + return SECFailure; 1.588 + 1.589 + signerinfos = cinfo->content.signedData->signerInfos; 1.590 + 1.591 + /* There has to be a signer, or it makes no sense. */ 1.592 + if (signerinfos == NULL || signerinfos[0] == NULL) 1.593 + return SECFailure; 1.594 + 1.595 + rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now()); 1.596 + if (rv != SECSuccess) 1.597 + return rv; 1.598 + 1.599 + attr = sec_pkcs7_create_attribute (cinfo->poolp, 1.600 + SEC_OID_PKCS9_SIGNING_TIME, 1.601 + &stime, PR_FALSE); 1.602 + SECITEM_FreeItem (&stime, PR_FALSE); 1.603 + 1.604 + if (attr == NULL) 1.605 + return SECFailure; 1.606 + 1.607 + rv = SECSuccess; 1.608 + for (si = 0; signerinfos[si] != NULL; si++) { 1.609 + SEC_PKCS7Attribute *oattr; 1.610 + 1.611 + oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr, 1.612 + SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE); 1.613 + PORT_Assert (oattr == NULL); 1.614 + if (oattr != NULL) 1.615 + continue; /* XXX or would it be better to replace it? */ 1.616 + 1.617 + rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr), 1.618 + attr); 1.619 + if (rv != SECSuccess) 1.620 + break; /* could try to continue, but may as well give up now */ 1.621 + } 1.622 + 1.623 + return rv; 1.624 +} 1.625 + 1.626 + 1.627 +/* 1.628 + * Add the specified attribute to the authenticated (i.e. signed) attributes 1.629 + * of "cinfo" -- "oidtag" describes the attribute and "value" is the 1.630 + * value to be associated with it. NOTE! "value" must already be encoded; 1.631 + * no interpretation of "oidtag" is done. Also, it is assumed that this 1.632 + * signedData has only one signer -- if we ever need to add attributes 1.633 + * when there is more than one signature, we need a way to specify *which* 1.634 + * signature should get the attribute. 1.635 + * 1.636 + * XXX Technically, a signed attribute can have multiple values; if/when 1.637 + * we ever need to support an attribute which takes multiple values, we 1.638 + * either need to change this interface or create an AddSignedAttributeValue 1.639 + * which can be called subsequently, and would then append a value. 1.640 + * 1.641 + * "cinfo" should be of type signedData (the only kind of pkcs7 data 1.642 + * that is allowed authenticated attributes); SECFailure will be returned 1.643 + * if it is not. 1.644 + */ 1.645 +SECStatus 1.646 +SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, 1.647 + SECOidTag oidtag, 1.648 + SECItem *value) 1.649 +{ 1.650 + SEC_PKCS7SignerInfo **signerinfos; 1.651 + SEC_PKCS7Attribute *attr; 1.652 + 1.653 + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); 1.654 + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) 1.655 + return SECFailure; 1.656 + 1.657 + signerinfos = cinfo->content.signedData->signerInfos; 1.658 + 1.659 + /* 1.660 + * No signature or more than one means no deal. 1.661 + */ 1.662 + if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) 1.663 + return SECFailure; 1.664 + 1.665 + attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE); 1.666 + if (attr == NULL) 1.667 + return SECFailure; 1.668 + 1.669 + return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr); 1.670 +} 1.671 + 1.672 + 1.673 +/* 1.674 + * Mark that the signer certificates and their issuing chain should 1.675 + * be included in the encoded data. This is expected to be used 1.676 + * in outgoing signed messages for email (S/MIME). 1.677 + * 1.678 + * "certdb" is the cert database to use for finding the chain. 1.679 + * It can be NULL, meaning use the default database. 1.680 + * 1.681 + * "cinfo" should be of type signedData or signedAndEnvelopedData; 1.682 + * SECFailure will be returned if it is not. 1.683 + */ 1.684 +SECStatus 1.685 +SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, 1.686 + CERTCertDBHandle *certdb) 1.687 +{ 1.688 + SECOidTag kind; 1.689 + SEC_PKCS7SignerInfo *signerinfo, **signerinfos; 1.690 + 1.691 + kind = SEC_PKCS7ContentType (cinfo); 1.692 + switch (kind) { 1.693 + case SEC_OID_PKCS7_SIGNED_DATA: 1.694 + signerinfos = cinfo->content.signedData->signerInfos; 1.695 + break; 1.696 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.697 + signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; 1.698 + break; 1.699 + default: 1.700 + return SECFailure; /* XXX set an error? */ 1.701 + } 1.702 + 1.703 + if (signerinfos == NULL) /* no signer, no certs? */ 1.704 + return SECFailure; /* XXX set an error? */ 1.705 + 1.706 + if (certdb == NULL) { 1.707 + certdb = CERT_GetDefaultCertDB(); 1.708 + if (certdb == NULL) { 1.709 + PORT_SetError (SEC_ERROR_BAD_DATABASE); 1.710 + return SECFailure; 1.711 + } 1.712 + } 1.713 + 1.714 + /* XXX Should it be an error if we find no signerinfo or no certs? */ 1.715 + while ((signerinfo = *signerinfos++) != NULL) { 1.716 + if (signerinfo->cert != NULL) 1.717 + /* get the cert chain. don't send the root to avoid contamination 1.718 + * of old clients with a new root that they don't trust 1.719 + */ 1.720 + signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert, 1.721 + certUsageEmailSigner, 1.722 + PR_FALSE); 1.723 + } 1.724 + 1.725 + return SECSuccess; 1.726 +} 1.727 + 1.728 + 1.729 +/* 1.730 + * Helper function to add a certificate chain for inclusion in the 1.731 + * bag of certificates in a signedData. 1.732 + */ 1.733 +static SECStatus 1.734 +sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo, 1.735 + CERTCertificate *cert, 1.736 + CERTCertDBHandle *certdb) 1.737 +{ 1.738 + SECOidTag kind; 1.739 + CERTCertificateList *certlist, **certlists, ***certlistsp; 1.740 + int count; 1.741 + 1.742 + kind = SEC_PKCS7ContentType (cinfo); 1.743 + switch (kind) { 1.744 + case SEC_OID_PKCS7_SIGNED_DATA: 1.745 + { 1.746 + SEC_PKCS7SignedData *sdp; 1.747 + 1.748 + sdp = cinfo->content.signedData; 1.749 + certlistsp = &(sdp->certLists); 1.750 + } 1.751 + break; 1.752 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.753 + { 1.754 + SEC_PKCS7SignedAndEnvelopedData *saedp; 1.755 + 1.756 + saedp = cinfo->content.signedAndEnvelopedData; 1.757 + certlistsp = &(saedp->certLists); 1.758 + } 1.759 + break; 1.760 + default: 1.761 + return SECFailure; /* XXX set an error? */ 1.762 + } 1.763 + 1.764 + if (certdb == NULL) { 1.765 + certdb = CERT_GetDefaultCertDB(); 1.766 + if (certdb == NULL) { 1.767 + PORT_SetError (SEC_ERROR_BAD_DATABASE); 1.768 + return SECFailure; 1.769 + } 1.770 + } 1.771 + 1.772 + certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE); 1.773 + if (certlist == NULL) 1.774 + return SECFailure; 1.775 + 1.776 + certlists = *certlistsp; 1.777 + if (certlists == NULL) { 1.778 + count = 0; 1.779 + certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp, 1.780 + 2 * sizeof(CERTCertificateList *)); 1.781 + } else { 1.782 + for (count = 0; certlists[count] != NULL; count++) 1.783 + ; 1.784 + PORT_Assert (count); /* should be at least one already */ 1.785 + certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, 1.786 + certlists, 1.787 + (count + 1) * sizeof(CERTCertificateList *), 1.788 + (count + 2) * sizeof(CERTCertificateList *)); 1.789 + } 1.790 + 1.791 + if (certlists == NULL) { 1.792 + CERT_DestroyCertificateList (certlist); 1.793 + return SECFailure; 1.794 + } 1.795 + 1.796 + certlists[count] = certlist; 1.797 + certlists[count + 1] = NULL; 1.798 + 1.799 + *certlistsp = certlists; 1.800 + 1.801 + return SECSuccess; 1.802 +} 1.803 + 1.804 + 1.805 +/* 1.806 + * Helper function to add a certificate for inclusion in the bag of 1.807 + * certificates in a signedData. 1.808 + */ 1.809 +static SECStatus 1.810 +sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo, 1.811 + CERTCertificate *cert) 1.812 +{ 1.813 + SECOidTag kind; 1.814 + CERTCertificate **certs, ***certsp; 1.815 + int count; 1.816 + 1.817 + kind = SEC_PKCS7ContentType (cinfo); 1.818 + switch (kind) { 1.819 + case SEC_OID_PKCS7_SIGNED_DATA: 1.820 + { 1.821 + SEC_PKCS7SignedData *sdp; 1.822 + 1.823 + sdp = cinfo->content.signedData; 1.824 + certsp = &(sdp->certs); 1.825 + } 1.826 + break; 1.827 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.828 + { 1.829 + SEC_PKCS7SignedAndEnvelopedData *saedp; 1.830 + 1.831 + saedp = cinfo->content.signedAndEnvelopedData; 1.832 + certsp = &(saedp->certs); 1.833 + } 1.834 + break; 1.835 + default: 1.836 + return SECFailure; /* XXX set an error? */ 1.837 + } 1.838 + 1.839 + cert = CERT_DupCertificate (cert); 1.840 + if (cert == NULL) 1.841 + return SECFailure; 1.842 + 1.843 + certs = *certsp; 1.844 + if (certs == NULL) { 1.845 + count = 0; 1.846 + certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, 1.847 + 2 * sizeof(CERTCertificate *)); 1.848 + } else { 1.849 + for (count = 0; certs[count] != NULL; count++) 1.850 + ; 1.851 + PORT_Assert (count); /* should be at least one already */ 1.852 + certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs, 1.853 + (count + 1) * sizeof(CERTCertificate *), 1.854 + (count + 2) * sizeof(CERTCertificate *)); 1.855 + } 1.856 + 1.857 + if (certs == NULL) { 1.858 + CERT_DestroyCertificate (cert); 1.859 + return SECFailure; 1.860 + } 1.861 + 1.862 + certs[count] = cert; 1.863 + certs[count + 1] = NULL; 1.864 + 1.865 + *certsp = certs; 1.866 + 1.867 + return SECSuccess; 1.868 +} 1.869 + 1.870 + 1.871 +/* 1.872 + * Create a PKCS7 certs-only container. 1.873 + * 1.874 + * "cert" is the (first) cert that will be included. 1.875 + * 1.876 + * "include_chain" specifies whether the entire chain for "cert" should 1.877 + * be included. 1.878 + * 1.879 + * "certdb" is the cert database to use for finding the chain. 1.880 + * It can be NULL in when "include_chain" is false, or when meaning 1.881 + * use the default database. 1.882 + * 1.883 + * More certs and chains can be added via AddCertificate and AddCertChain. 1.884 + * 1.885 + * An error results in a return value of NULL and an error set. 1.886 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.887 + */ 1.888 +SEC_PKCS7ContentInfo * 1.889 +SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, 1.890 + PRBool include_chain, 1.891 + CERTCertDBHandle *certdb) 1.892 +{ 1.893 + SEC_PKCS7ContentInfo *cinfo; 1.894 + SECStatus rv; 1.895 + 1.896 + cinfo = sec_pkcs7_create_signed_data (NULL, NULL); 1.897 + if (cinfo == NULL) 1.898 + return NULL; 1.899 + 1.900 + if (include_chain) 1.901 + rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb); 1.902 + else 1.903 + rv = sec_pkcs7_add_certificate (cinfo, cert); 1.904 + 1.905 + if (rv != SECSuccess) { 1.906 + SEC_PKCS7DestroyContentInfo (cinfo); 1.907 + return NULL; 1.908 + } 1.909 + 1.910 + return cinfo; 1.911 +} 1.912 + 1.913 + 1.914 +/* 1.915 + * Add "cert" and its entire chain to the set of certs included in "cinfo". 1.916 + * 1.917 + * "certdb" is the cert database to use for finding the chain. 1.918 + * It can be NULL, meaning use the default database. 1.919 + * 1.920 + * "cinfo" should be of type signedData or signedAndEnvelopedData; 1.921 + * SECFailure will be returned if it is not. 1.922 + */ 1.923 +SECStatus 1.924 +SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, 1.925 + CERTCertificate *cert, 1.926 + CERTCertDBHandle *certdb) 1.927 +{ 1.928 + SECOidTag kind; 1.929 + 1.930 + kind = SEC_PKCS7ContentType (cinfo); 1.931 + if (kind != SEC_OID_PKCS7_SIGNED_DATA 1.932 + && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) 1.933 + return SECFailure; /* XXX set an error? */ 1.934 + 1.935 + return sec_pkcs7_add_cert_chain (cinfo, cert, certdb); 1.936 +} 1.937 + 1.938 + 1.939 +/* 1.940 + * Add "cert" to the set of certs included in "cinfo". 1.941 + * 1.942 + * "cinfo" should be of type signedData or signedAndEnvelopedData; 1.943 + * SECFailure will be returned if it is not. 1.944 + */ 1.945 +SECStatus 1.946 +SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert) 1.947 +{ 1.948 + SECOidTag kind; 1.949 + 1.950 + kind = SEC_PKCS7ContentType (cinfo); 1.951 + if (kind != SEC_OID_PKCS7_SIGNED_DATA 1.952 + && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) 1.953 + return SECFailure; /* XXX set an error? */ 1.954 + 1.955 + return sec_pkcs7_add_certificate (cinfo, cert); 1.956 +} 1.957 + 1.958 + 1.959 +static SECStatus 1.960 +sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo, 1.961 + PLArenaPool *poolp, 1.962 + SECOidTag kind, PRBool detached, 1.963 + SECOidTag encalg, int keysize) 1.964 +{ 1.965 + SECStatus rv; 1.966 + 1.967 + PORT_Assert (enccinfo != NULL && poolp != NULL); 1.968 + if (enccinfo == NULL || poolp == NULL) 1.969 + return SECFailure; 1.970 + 1.971 + /* 1.972 + * XXX Some day we may want to allow for other kinds. That needs 1.973 + * more work and modifications to the creation interface, etc. 1.974 + * For now, allow but notice callers who pass in other kinds. 1.975 + * They are responsible for creating the inner type and encoding, 1.976 + * if it is other than DATA. 1.977 + */ 1.978 + PORT_Assert (kind == SEC_OID_PKCS7_DATA); 1.979 + 1.980 + enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind); 1.981 + PORT_Assert (enccinfo->contentTypeTag 1.982 + && enccinfo->contentTypeTag->offset == kind); 1.983 + 1.984 + rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType), 1.985 + &(enccinfo->contentTypeTag->oid)); 1.986 + if (rv != SECSuccess) 1.987 + return rv; 1.988 + 1.989 + /* Save keysize and algorithm for later. */ 1.990 + enccinfo->keysize = keysize; 1.991 + enccinfo->encalg = encalg; 1.992 + 1.993 + return SECSuccess; 1.994 +} 1.995 + 1.996 + 1.997 +/* 1.998 + * Add a recipient to a PKCS7 thing, verifying their cert first. 1.999 + * Any error returns SECFailure. 1.1000 + */ 1.1001 +static SECStatus 1.1002 +sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo, 1.1003 + CERTCertificate *cert, 1.1004 + SECCertUsage certusage, 1.1005 + CERTCertDBHandle *certdb) 1.1006 +{ 1.1007 + SECOidTag kind; 1.1008 + SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp; 1.1009 + SECItem *dummy; 1.1010 + void *mark; 1.1011 + int count; 1.1012 + 1.1013 + kind = SEC_PKCS7ContentType (cinfo); 1.1014 + switch (kind) { 1.1015 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.1016 + { 1.1017 + SEC_PKCS7EnvelopedData *edp; 1.1018 + 1.1019 + edp = cinfo->content.envelopedData; 1.1020 + recipientinfosp = &(edp->recipientInfos); 1.1021 + } 1.1022 + break; 1.1023 + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 1.1024 + { 1.1025 + SEC_PKCS7SignedAndEnvelopedData *saedp; 1.1026 + 1.1027 + saedp = cinfo->content.signedAndEnvelopedData; 1.1028 + recipientinfosp = &(saedp->recipientInfos); 1.1029 + } 1.1030 + break; 1.1031 + default: 1.1032 + return SECFailure; /* XXX set an error? */ 1.1033 + } 1.1034 + 1.1035 + /* 1.1036 + * XXX I think that CERT_VerifyCert should do this if *it* is passed 1.1037 + * a NULL database. 1.1038 + */ 1.1039 + if (certdb == NULL) { 1.1040 + certdb = CERT_GetDefaultCertDB(); 1.1041 + if (certdb == NULL) 1.1042 + return SECFailure; /* XXX set an error? */ 1.1043 + } 1.1044 + 1.1045 + if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), 1.1046 + cinfo->pwfn_arg, NULL) != SECSuccess) 1.1047 + { 1.1048 + /* XXX Did CERT_VerifyCert set an error? */ 1.1049 + return SECFailure; 1.1050 + } 1.1051 + 1.1052 + mark = PORT_ArenaMark (cinfo->poolp); 1.1053 + 1.1054 + recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp, 1.1055 + sizeof(SEC_PKCS7RecipientInfo)); 1.1056 + if (recipientinfo == NULL) { 1.1057 + PORT_ArenaRelease (cinfo->poolp, mark); 1.1058 + return SECFailure; 1.1059 + } 1.1060 + 1.1061 + dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version, 1.1062 + SEC_PKCS7_RECIPIENT_INFO_VERSION); 1.1063 + if (dummy == NULL) { 1.1064 + PORT_ArenaRelease (cinfo->poolp, mark); 1.1065 + return SECFailure; 1.1066 + } 1.1067 + PORT_Assert (dummy == &recipientinfo->version); 1.1068 + 1.1069 + recipientinfo->cert = CERT_DupCertificate (cert); 1.1070 + if (recipientinfo->cert == NULL) { 1.1071 + PORT_ArenaRelease (cinfo->poolp, mark); 1.1072 + return SECFailure; 1.1073 + } 1.1074 + 1.1075 + recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); 1.1076 + if (recipientinfo->issuerAndSN == NULL) { 1.1077 + PORT_ArenaRelease (cinfo->poolp, mark); 1.1078 + return SECFailure; 1.1079 + } 1.1080 + 1.1081 + /* 1.1082 + * Okay, now recipientinfo is all set. We just need to put it into 1.1083 + * the main structure. 1.1084 + * 1.1085 + * If this is the first recipient, allocate a new recipientinfos array; 1.1086 + * otherwise, reallocate the array, making room for the new entry. 1.1087 + */ 1.1088 + recipientinfos = *recipientinfosp; 1.1089 + if (recipientinfos == NULL) { 1.1090 + count = 0; 1.1091 + recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc ( 1.1092 + cinfo->poolp, 1.1093 + 2 * sizeof(SEC_PKCS7RecipientInfo *)); 1.1094 + } else { 1.1095 + for (count = 0; recipientinfos[count] != NULL; count++) 1.1096 + ; 1.1097 + PORT_Assert (count); /* should be at least one already */ 1.1098 + recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow ( 1.1099 + cinfo->poolp, recipientinfos, 1.1100 + (count + 1) * sizeof(SEC_PKCS7RecipientInfo *), 1.1101 + (count + 2) * sizeof(SEC_PKCS7RecipientInfo *)); 1.1102 + } 1.1103 + 1.1104 + if (recipientinfos == NULL) { 1.1105 + PORT_ArenaRelease (cinfo->poolp, mark); 1.1106 + return SECFailure; 1.1107 + } 1.1108 + 1.1109 + recipientinfos[count] = recipientinfo; 1.1110 + recipientinfos[count + 1] = NULL; 1.1111 + 1.1112 + *recipientinfosp = recipientinfos; 1.1113 + 1.1114 + PORT_ArenaUnmark (cinfo->poolp, mark); 1.1115 + return SECSuccess; 1.1116 +} 1.1117 + 1.1118 + 1.1119 +/* 1.1120 + * Start a PKCS7 enveloping context. 1.1121 + * 1.1122 + * "cert" is the cert for the recipient. It will be checked for validity. 1.1123 + * 1.1124 + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) 1.1125 + * XXX Maybe SECCertUsage should be split so that our caller just says 1.1126 + * "email" and *we* add the "recipient" part -- otherwise our caller 1.1127 + * could be lying about the usage; we do not want to allow encryption 1.1128 + * certs for signing or vice versa. 1.1129 + * 1.1130 + * "certdb" is the cert database to use for verifying the cert. 1.1131 + * It can be NULL if a default database is available (like in the client). 1.1132 + * 1.1133 + * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). 1.1134 + * 1.1135 + * "keysize" specifies the bulk encryption key size, in bits. 1.1136 + * 1.1137 + * The return value can be passed to functions which add things to 1.1138 + * it like more recipients, then eventually to SEC_PKCS7Encode() or to 1.1139 + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to 1.1140 + * SEC_PKCS7DestroyContentInfo(). 1.1141 + * 1.1142 + * An error results in a return value of NULL and an error set. 1.1143 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.1144 + */ 1.1145 +extern SEC_PKCS7ContentInfo * 1.1146 +SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, 1.1147 + SECCertUsage certusage, 1.1148 + CERTCertDBHandle *certdb, 1.1149 + SECOidTag encalg, 1.1150 + int keysize, 1.1151 + SECKEYGetPasswordKey pwfn, void *pwfn_arg) 1.1152 +{ 1.1153 + SEC_PKCS7ContentInfo *cinfo; 1.1154 + SEC_PKCS7EnvelopedData *envd; 1.1155 + SECStatus rv; 1.1156 + 1.1157 + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA, 1.1158 + PR_FALSE, pwfn, pwfn_arg); 1.1159 + if (cinfo == NULL) 1.1160 + return NULL; 1.1161 + 1.1162 + rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); 1.1163 + if (rv != SECSuccess) { 1.1164 + SEC_PKCS7DestroyContentInfo (cinfo); 1.1165 + return NULL; 1.1166 + } 1.1167 + 1.1168 + envd = cinfo->content.envelopedData; 1.1169 + PORT_Assert (envd != NULL); 1.1170 + 1.1171 + /* 1.1172 + * XXX Might we want to allow content types other than data? 1.1173 + * If so, via what interface? 1.1174 + */ 1.1175 + rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo), 1.1176 + cinfo->poolp, 1.1177 + SEC_OID_PKCS7_DATA, PR_FALSE, 1.1178 + encalg, keysize); 1.1179 + if (rv != SECSuccess) { 1.1180 + SEC_PKCS7DestroyContentInfo (cinfo); 1.1181 + return NULL; 1.1182 + } 1.1183 + 1.1184 + /* XXX Anything more to do here? */ 1.1185 + 1.1186 + return cinfo; 1.1187 +} 1.1188 + 1.1189 + 1.1190 +/* 1.1191 + * Add another recipient to an encrypted message. 1.1192 + * 1.1193 + * "cinfo" should be of type envelopedData or signedAndEnvelopedData; 1.1194 + * SECFailure will be returned if it is not. 1.1195 + * 1.1196 + * "cert" is the cert for the recipient. It will be checked for validity. 1.1197 + * 1.1198 + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) 1.1199 + * XXX Maybe SECCertUsage should be split so that our caller just says 1.1200 + * "email" and *we* add the "recipient" part -- otherwise our caller 1.1201 + * could be lying about the usage; we do not want to allow encryption 1.1202 + * certs for signing or vice versa. 1.1203 + * 1.1204 + * "certdb" is the cert database to use for verifying the cert. 1.1205 + * It can be NULL if a default database is available (like in the client). 1.1206 + */ 1.1207 +SECStatus 1.1208 +SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, 1.1209 + CERTCertificate *cert, 1.1210 + SECCertUsage certusage, 1.1211 + CERTCertDBHandle *certdb) 1.1212 +{ 1.1213 + return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); 1.1214 +} 1.1215 + 1.1216 + 1.1217 +/* 1.1218 + * Create an empty PKCS7 data content info. 1.1219 + * 1.1220 + * An error results in a return value of NULL and an error set. 1.1221 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.1222 + */ 1.1223 +SEC_PKCS7ContentInfo * 1.1224 +SEC_PKCS7CreateData (void) 1.1225 +{ 1.1226 + return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE, 1.1227 + NULL, NULL); 1.1228 +} 1.1229 + 1.1230 + 1.1231 +/* 1.1232 + * Create an empty PKCS7 encrypted content info. 1.1233 + * 1.1234 + * "algorithm" specifies the bulk encryption algorithm to use. 1.1235 + * 1.1236 + * An error results in a return value of NULL and an error set. 1.1237 + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) 1.1238 + */ 1.1239 +SEC_PKCS7ContentInfo * 1.1240 +SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, 1.1241 + SECKEYGetPasswordKey pwfn, void *pwfn_arg) 1.1242 +{ 1.1243 + SEC_PKCS7ContentInfo *cinfo; 1.1244 + SECAlgorithmID *algid; 1.1245 + SEC_PKCS7EncryptedData *enc_data; 1.1246 + SECStatus rv; 1.1247 + 1.1248 + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, 1.1249 + PR_FALSE, pwfn, pwfn_arg); 1.1250 + if (cinfo == NULL) 1.1251 + return NULL; 1.1252 + 1.1253 + enc_data = cinfo->content.encryptedData; 1.1254 + algid = &(enc_data->encContentInfo.contentEncAlg); 1.1255 + 1.1256 + if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { 1.1257 + rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); 1.1258 + } else { 1.1259 + /* Assume password-based-encryption. 1.1260 + * Note: we can't generate pkcs5v2 from this interface. 1.1261 + * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting 1.1262 + * non-PBE oids and assuming that they are pkcs5v2 oids, but 1.1263 + * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular 1.1264 + * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData 1.1265 + * to create pkcs5v2 PBEs */ 1.1266 + SECAlgorithmID *pbe_algid; 1.1267 + pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1.1268 + NSS_PBE_DEFAULT_ITERATION_COUNT, 1.1269 + NULL); 1.1270 + if (pbe_algid == NULL) { 1.1271 + rv = SECFailure; 1.1272 + } else { 1.1273 + rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); 1.1274 + SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); 1.1275 + } 1.1276 + } 1.1277 + 1.1278 + if (rv != SECSuccess) { 1.1279 + SEC_PKCS7DestroyContentInfo (cinfo); 1.1280 + return NULL; 1.1281 + } 1.1282 + 1.1283 + rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo), 1.1284 + cinfo->poolp, 1.1285 + SEC_OID_PKCS7_DATA, PR_FALSE, 1.1286 + algorithm, keysize); 1.1287 + if (rv != SECSuccess) { 1.1288 + SEC_PKCS7DestroyContentInfo (cinfo); 1.1289 + return NULL; 1.1290 + } 1.1291 + 1.1292 + return cinfo; 1.1293 +} 1.1294 +