andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * PKCS7 creation. andre@0: */ andre@0: andre@0: #include "p7local.h" andre@0: andre@0: #include "cert.h" andre@0: #include "secasn1.h" andre@0: #include "secitem.h" andre@0: #include "secoid.h" andre@0: #include "pk11func.h" andre@0: #include "prtime.h" andre@0: #include "secerr.h" andre@0: #include "secder.h" andre@0: #include "secpkcs5.h" andre@0: andre@0: const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */ andre@0: andre@0: static SECStatus andre@0: sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PLArenaPool *poolp, andre@0: SECOidTag kind, PRBool detached) andre@0: { andre@0: void *thing; andre@0: int version; andre@0: SECItem *versionp; andre@0: SECStatus rv; andre@0: andre@0: PORT_Assert (cinfo != NULL && poolp != NULL); andre@0: if (cinfo == NULL || poolp == NULL) andre@0: return SECFailure; andre@0: andre@0: cinfo->contentTypeTag = SECOID_FindOIDByTag (kind); andre@0: PORT_Assert (cinfo->contentTypeTag andre@0: && cinfo->contentTypeTag->offset == kind); andre@0: andre@0: rv = SECITEM_CopyItem (poolp, &(cinfo->contentType), andre@0: &(cinfo->contentTypeTag->oid)); andre@0: if (rv != SECSuccess) andre@0: return rv; andre@0: andre@0: if (detached) andre@0: return SECSuccess; andre@0: andre@0: switch (kind) { andre@0: default: andre@0: case SEC_OID_PKCS7_DATA: andre@0: thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem)); andre@0: cinfo->content.data = (SECItem*)thing; andre@0: versionp = NULL; andre@0: version = -1; andre@0: break; andre@0: case SEC_OID_PKCS7_DIGESTED_DATA: andre@0: thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData)); andre@0: cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing; andre@0: versionp = &(cinfo->content.digestedData->version); andre@0: version = SEC_PKCS7_DIGESTED_DATA_VERSION; andre@0: break; andre@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: andre@0: thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData)); andre@0: cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing; andre@0: versionp = &(cinfo->content.encryptedData->version); andre@0: version = SEC_PKCS7_ENCRYPTED_DATA_VERSION; andre@0: break; andre@0: case SEC_OID_PKCS7_ENVELOPED_DATA: andre@0: thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData)); andre@0: cinfo->content.envelopedData = andre@0: (SEC_PKCS7EnvelopedData*)thing; andre@0: versionp = &(cinfo->content.envelopedData->version); andre@0: version = SEC_PKCS7_ENVELOPED_DATA_VERSION; andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_DATA: andre@0: thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData)); andre@0: cinfo->content.signedData = andre@0: (SEC_PKCS7SignedData*)thing; andre@0: versionp = &(cinfo->content.signedData->version); andre@0: version = SEC_PKCS7_SIGNED_DATA_VERSION; andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData)); andre@0: cinfo->content.signedAndEnvelopedData = andre@0: (SEC_PKCS7SignedAndEnvelopedData*)thing; andre@0: versionp = &(cinfo->content.signedAndEnvelopedData->version); andre@0: version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION; andre@0: break; andre@0: } andre@0: andre@0: if (thing == NULL) andre@0: return SECFailure; andre@0: andre@0: if (versionp != NULL) { andre@0: SECItem *dummy; andre@0: andre@0: PORT_Assert (version >= 0); andre@0: dummy = SEC_ASN1EncodeInteger (poolp, versionp, version); andre@0: if (dummy == NULL) andre@0: return SECFailure; andre@0: PORT_Assert (dummy == versionp); andre@0: } andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: static SEC_PKCS7ContentInfo * andre@0: sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached, andre@0: SECKEYGetPasswordKey pwfn, void *pwfn_arg) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: PLArenaPool *poolp; andre@0: SECStatus rv; andre@0: andre@0: poolp = PORT_NewArena (1024); /* XXX what is right value? */ andre@0: if (poolp == NULL) andre@0: return NULL; andre@0: andre@0: cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); andre@0: if (cinfo == NULL) { andre@0: PORT_FreeArena (poolp, PR_FALSE); andre@0: return NULL; andre@0: } andre@0: andre@0: cinfo->poolp = poolp; andre@0: cinfo->pwfn = pwfn; andre@0: cinfo->pwfn_arg = pwfn_arg; andre@0: cinfo->created = PR_TRUE; andre@0: cinfo->refCount = 1; andre@0: andre@0: rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached); andre@0: if (rv != SECSuccess) { andre@0: PORT_FreeArena (poolp, PR_FALSE); andre@0: return NULL; andre@0: } andre@0: andre@0: return cinfo; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add a signer to a PKCS7 thing, verifying the signature cert first. andre@0: * Any error returns SECFailure. andre@0: * andre@0: * XXX Right now this only adds the *first* signer. It fails if you try andre@0: * to add a second one -- this needs to be fixed. andre@0: */ andre@0: static SECStatus andre@0: sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate * cert, andre@0: SECCertUsage certusage, andre@0: CERTCertDBHandle * certdb, andre@0: SECOidTag digestalgtag, andre@0: SECItem * digestdata) andre@0: { andre@0: SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp; andre@0: SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp; andre@0: SECItem *digest, **digests, ***digestsp; andre@0: SECItem * dummy; andre@0: void * mark; andre@0: SECStatus rv; andre@0: SECOidTag kind; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: switch (kind) { andre@0: case SEC_OID_PKCS7_SIGNED_DATA: andre@0: { andre@0: SEC_PKCS7SignedData *sdp; andre@0: andre@0: sdp = cinfo->content.signedData; andre@0: digestalgsp = &(sdp->digestAlgorithms); andre@0: digestsp = &(sdp->digests); andre@0: signerinfosp = &(sdp->signerInfos); andre@0: } andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: { andre@0: SEC_PKCS7SignedAndEnvelopedData *saedp; andre@0: andre@0: saedp = cinfo->content.signedAndEnvelopedData; andre@0: digestalgsp = &(saedp->digestAlgorithms); andre@0: digestsp = &(saedp->digests); andre@0: signerinfosp = &(saedp->signerInfos); andre@0: } andre@0: break; andre@0: default: andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: /* andre@0: * XXX I think that CERT_VerifyCert should do this if *it* is passed andre@0: * a NULL database. andre@0: */ andre@0: if (certdb == NULL) { andre@0: certdb = CERT_GetDefaultCertDB(); andre@0: if (certdb == NULL) andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), andre@0: cinfo->pwfn_arg, NULL) != SECSuccess) andre@0: { andre@0: /* XXX Did CERT_VerifyCert set an error? */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * XXX This is the check that we do not already have a signer. andre@0: * This is not what we really want -- we want to allow this andre@0: * and *add* the new signer. andre@0: */ andre@0: PORT_Assert (*signerinfosp == NULL andre@0: && *digestalgsp == NULL && *digestsp == NULL); andre@0: if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL) andre@0: return SECFailure; andre@0: andre@0: mark = PORT_ArenaMark (cinfo->poolp); andre@0: andre@0: signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, andre@0: sizeof(SEC_PKCS7SignerInfo)); andre@0: if (signerinfo == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version, andre@0: SEC_PKCS7_SIGNER_INFO_VERSION); andre@0: if (dummy == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: PORT_Assert (dummy == &signerinfo->version); andre@0: andre@0: signerinfo->cert = CERT_DupCertificate (cert); andre@0: if (signerinfo->cert == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); andre@0: if (signerinfo->issuerAndSN == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg, andre@0: digestalgtag, NULL); andre@0: if (rv != SECSuccess) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * Okay, now signerinfo is all set. We just need to put it and its andre@0: * companions (another copy of the digest algorithm, and the digest andre@0: * itself if given) into the main structure. andre@0: * andre@0: * XXX If we are handling more than one signer, the following code andre@0: * needs to look through the digest algorithms already specified andre@0: * and see if the same one is there already. If it is, it does not andre@0: * need to be added again. Also, if it is there *and* the digest andre@0: * is not null, then the digest given should match the digest already andre@0: * specified -- if not, that is an error. Finally, the new signerinfo andre@0: * should be *added* to the set already found. andre@0: */ andre@0: andre@0: signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp, andre@0: 2 * sizeof(SEC_PKCS7SignerInfo *)); andre@0: if (signerinfos == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: signerinfos[0] = signerinfo; andre@0: signerinfos[1] = NULL; andre@0: andre@0: digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID)); andre@0: digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *)); andre@0: if (digestalg == NULL || digestalgs == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL); andre@0: if (rv != SECSuccess) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: digestalgs[0] = digestalg; andre@0: digestalgs[1] = NULL; andre@0: andre@0: if (digestdata != NULL) { andre@0: digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem)); andre@0: digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, andre@0: 2 * sizeof(SECItem *)); andre@0: if (digest == NULL || digests == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata); andre@0: if (rv != SECSuccess) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: digests[0] = digest; andre@0: digests[1] = NULL; andre@0: } else { andre@0: digests = NULL; andre@0: } andre@0: andre@0: *signerinfosp = signerinfos; andre@0: *digestalgsp = digestalgs; andre@0: *digestsp = digests; andre@0: andre@0: PORT_ArenaUnmark(cinfo->poolp, mark); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Helper function for creating an empty signedData. andre@0: */ andre@0: static SEC_PKCS7ContentInfo * andre@0: sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: SEC_PKCS7SignedData *sigd; andre@0: SECStatus rv; andre@0: andre@0: cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE, andre@0: pwfn, pwfn_arg); andre@0: if (cinfo == NULL) andre@0: return NULL; andre@0: andre@0: sigd = cinfo->content.signedData; andre@0: PORT_Assert (sigd != NULL); andre@0: andre@0: /* andre@0: * XXX Might we want to allow content types other than data? andre@0: * If so, via what interface? andre@0: */ andre@0: rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp, andre@0: SEC_OID_PKCS7_DATA, PR_TRUE); andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: return cinfo; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Start a PKCS7 signing context. andre@0: * andre@0: * "cert" is the cert that will be used to sign the data. It will be andre@0: * checked for validity. andre@0: * andre@0: * "certusage" describes the signing usage (e.g. certUsageEmailSigner) andre@0: * XXX Maybe SECCertUsage should be split so that our caller just says andre@0: * "email" and *we* add the "signing" part -- otherwise our caller andre@0: * could be lying about the usage; we do not want to allow encryption andre@0: * certs for signing or vice versa. andre@0: * andre@0: * "certdb" is the cert database to use for verifying the cert. andre@0: * It can be NULL if a default database is available (like in the client). andre@0: * andre@0: * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). andre@0: * andre@0: * "digest" is the actual digest of the data. It must be provided in andre@0: * the case of detached data or NULL if the content will be included. andre@0: * andre@0: * The return value can be passed to functions which add things to andre@0: * it like attributes, then eventually to SEC_PKCS7Encode() or to andre@0: * SEC_PKCS7EncoderStart() to create the encoded data, and finally to andre@0: * SEC_PKCS7DestroyContentInfo(). andre@0: * andre@0: * An error results in a return value of NULL and an error set. andre@0: * (Retrieve specific errors via PORT_GetError()/XP_GetError().) andre@0: */ andre@0: SEC_PKCS7ContentInfo * andre@0: SEC_PKCS7CreateSignedData (CERTCertificate *cert, andre@0: SECCertUsage certusage, andre@0: CERTCertDBHandle *certdb, andre@0: SECOidTag digestalg, andre@0: SECItem *digest, andre@0: SECKEYGetPasswordKey pwfn, void *pwfn_arg) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: SECStatus rv; andre@0: andre@0: cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg); andre@0: if (cinfo == NULL) andre@0: return NULL; andre@0: andre@0: rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb, andre@0: digestalg, digest); andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: return cinfo; andre@0: } andre@0: andre@0: andre@0: static SEC_PKCS7Attribute * andre@0: sec_pkcs7_create_attribute (PLArenaPool *poolp, SECOidTag oidtag, andre@0: SECItem *value, PRBool encoded) andre@0: { andre@0: SEC_PKCS7Attribute *attr; andre@0: SECItem **values; andre@0: void *mark; andre@0: andre@0: PORT_Assert (poolp != NULL); andre@0: mark = PORT_ArenaMark (poolp); andre@0: andre@0: attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, andre@0: sizeof(SEC_PKCS7Attribute)); andre@0: if (attr == NULL) andre@0: goto loser; andre@0: andre@0: attr->typeTag = SECOID_FindOIDByTag (oidtag); andre@0: if (attr->typeTag == NULL) andre@0: goto loser; andre@0: andre@0: if (SECITEM_CopyItem (poolp, &(attr->type), andre@0: &(attr->typeTag->oid)) != SECSuccess) andre@0: goto loser; andre@0: andre@0: values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *)); andre@0: if (values == NULL) andre@0: goto loser; andre@0: andre@0: if (value != NULL) { andre@0: SECItem *copy; andre@0: andre@0: copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); andre@0: if (copy == NULL) andre@0: goto loser; andre@0: andre@0: if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess) andre@0: goto loser; andre@0: andre@0: value = copy; andre@0: } andre@0: andre@0: values[0] = value; andre@0: values[1] = NULL; andre@0: attr->values = values; andre@0: attr->encoded = encoded; andre@0: andre@0: PORT_ArenaUnmark (poolp, mark); andre@0: return attr; andre@0: andre@0: loser: andre@0: PORT_Assert (mark != NULL); andre@0: PORT_ArenaRelease (poolp, mark); andre@0: return NULL; andre@0: } andre@0: andre@0: andre@0: static SECStatus andre@0: sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo, andre@0: SEC_PKCS7Attribute ***attrsp, andre@0: SEC_PKCS7Attribute *attr) andre@0: { andre@0: SEC_PKCS7Attribute **attrs; andre@0: SECItem *ct_value; andre@0: void *mark; andre@0: andre@0: PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); andre@0: if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) andre@0: return SECFailure; andre@0: andre@0: attrs = *attrsp; andre@0: if (attrs != NULL) { andre@0: int count; andre@0: andre@0: /* andre@0: * We already have some attributes, and just need to add this andre@0: * new one. andre@0: */ andre@0: andre@0: /* andre@0: * We should already have the *required* attributes, which were andre@0: * created/added at the same time the first attribute was added. andre@0: */ andre@0: PORT_Assert (sec_PKCS7FindAttribute (attrs, andre@0: SEC_OID_PKCS9_CONTENT_TYPE, andre@0: PR_FALSE) != NULL); andre@0: PORT_Assert (sec_PKCS7FindAttribute (attrs, andre@0: SEC_OID_PKCS9_MESSAGE_DIGEST, andre@0: PR_FALSE) != NULL); andre@0: andre@0: for (count = 0; attrs[count] != NULL; count++) andre@0: ; andre@0: attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs, andre@0: (count + 1) * sizeof(SEC_PKCS7Attribute *), andre@0: (count + 2) * sizeof(SEC_PKCS7Attribute *)); andre@0: if (attrs == NULL) andre@0: return SECFailure; andre@0: andre@0: attrs[count] = attr; andre@0: attrs[count+1] = NULL; andre@0: *attrsp = attrs; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* andre@0: * This is the first time an attribute is going in. andre@0: * We need to create and add the required attributes, and then andre@0: * we will also add in the one our caller gave us. andre@0: */ andre@0: andre@0: /* andre@0: * There are 2 required attributes, plus the one our caller wants andre@0: * to add, plus we always end with a NULL one. Thus, four slots. andre@0: */ andre@0: attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, andre@0: 4 * sizeof(SEC_PKCS7Attribute *)); andre@0: if (attrs == NULL) andre@0: return SECFailure; andre@0: andre@0: mark = PORT_ArenaMark (cinfo->poolp); andre@0: andre@0: /* andre@0: * First required attribute is the content type of the data andre@0: * being signed. andre@0: */ andre@0: ct_value = &(cinfo->content.signedData->contentInfo.contentType); andre@0: attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp, andre@0: SEC_OID_PKCS9_CONTENT_TYPE, andre@0: ct_value, PR_FALSE); andre@0: /* andre@0: * Second required attribute is the message digest of the data andre@0: * being signed; we leave the value NULL for now (just create andre@0: * the place for it to go), and the encoder will fill it in later. andre@0: */ andre@0: attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp, andre@0: SEC_OID_PKCS9_MESSAGE_DIGEST, andre@0: NULL, PR_FALSE); andre@0: if (attrs[0] == NULL || attrs[1] == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: attrs[2] = attr; andre@0: attrs[3] = NULL; andre@0: *attrsp = attrs; andre@0: andre@0: PORT_ArenaUnmark (cinfo->poolp, mark); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add the signing time to the authenticated (i.e. signed) attributes andre@0: * of "cinfo". This is expected to be included in outgoing signed andre@0: * messages for email (S/MIME) but is likely useful in other situations. andre@0: * andre@0: * This should only be added once; a second call will either do andre@0: * nothing or replace an old signing time with a newer one. andre@0: * andre@0: * XXX This will probably just shove the current time into "cinfo" andre@0: * but it will not actually get signed until the entire item is andre@0: * processed for encoding. Is this (expected to be small) delay okay? andre@0: * andre@0: * "cinfo" should be of type signedData (the only kind of pkcs7 data andre@0: * that is allowed authenticated attributes); SECFailure will be returned andre@0: * if it is not. andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo) andre@0: { andre@0: SEC_PKCS7SignerInfo **signerinfos; andre@0: SEC_PKCS7Attribute *attr; andre@0: SECItem stime; andre@0: SECStatus rv; andre@0: int si; andre@0: andre@0: PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); andre@0: if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) andre@0: return SECFailure; andre@0: andre@0: signerinfos = cinfo->content.signedData->signerInfos; andre@0: andre@0: /* There has to be a signer, or it makes no sense. */ andre@0: if (signerinfos == NULL || signerinfos[0] == NULL) andre@0: return SECFailure; andre@0: andre@0: rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now()); andre@0: if (rv != SECSuccess) andre@0: return rv; andre@0: andre@0: attr = sec_pkcs7_create_attribute (cinfo->poolp, andre@0: SEC_OID_PKCS9_SIGNING_TIME, andre@0: &stime, PR_FALSE); andre@0: SECITEM_FreeItem (&stime, PR_FALSE); andre@0: andre@0: if (attr == NULL) andre@0: return SECFailure; andre@0: andre@0: rv = SECSuccess; andre@0: for (si = 0; signerinfos[si] != NULL; si++) { andre@0: SEC_PKCS7Attribute *oattr; andre@0: andre@0: oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr, andre@0: SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE); andre@0: PORT_Assert (oattr == NULL); andre@0: if (oattr != NULL) andre@0: continue; /* XXX or would it be better to replace it? */ andre@0: andre@0: rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr), andre@0: attr); andre@0: if (rv != SECSuccess) andre@0: break; /* could try to continue, but may as well give up now */ andre@0: } andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add the specified attribute to the authenticated (i.e. signed) attributes andre@0: * of "cinfo" -- "oidtag" describes the attribute and "value" is the andre@0: * value to be associated with it. NOTE! "value" must already be encoded; andre@0: * no interpretation of "oidtag" is done. Also, it is assumed that this andre@0: * signedData has only one signer -- if we ever need to add attributes andre@0: * when there is more than one signature, we need a way to specify *which* andre@0: * signature should get the attribute. andre@0: * andre@0: * XXX Technically, a signed attribute can have multiple values; if/when andre@0: * we ever need to support an attribute which takes multiple values, we andre@0: * either need to change this interface or create an AddSignedAttributeValue andre@0: * which can be called subsequently, and would then append a value. andre@0: * andre@0: * "cinfo" should be of type signedData (the only kind of pkcs7 data andre@0: * that is allowed authenticated attributes); SECFailure will be returned andre@0: * if it is not. andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, andre@0: SECOidTag oidtag, andre@0: SECItem *value) andre@0: { andre@0: SEC_PKCS7SignerInfo **signerinfos; andre@0: SEC_PKCS7Attribute *attr; andre@0: andre@0: PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); andre@0: if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) andre@0: return SECFailure; andre@0: andre@0: signerinfos = cinfo->content.signedData->signerInfos; andre@0: andre@0: /* andre@0: * No signature or more than one means no deal. andre@0: */ andre@0: if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) andre@0: return SECFailure; andre@0: andre@0: attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE); andre@0: if (attr == NULL) andre@0: return SECFailure; andre@0: andre@0: return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Mark that the signer certificates and their issuing chain should andre@0: * be included in the encoded data. This is expected to be used andre@0: * in outgoing signed messages for email (S/MIME). andre@0: * andre@0: * "certdb" is the cert database to use for finding the chain. andre@0: * It can be NULL, meaning use the default database. andre@0: * andre@0: * "cinfo" should be of type signedData or signedAndEnvelopedData; andre@0: * SECFailure will be returned if it is not. andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: SECOidTag kind; andre@0: SEC_PKCS7SignerInfo *signerinfo, **signerinfos; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: switch (kind) { andre@0: case SEC_OID_PKCS7_SIGNED_DATA: andre@0: signerinfos = cinfo->content.signedData->signerInfos; andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; andre@0: break; andre@0: default: andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: if (signerinfos == NULL) /* no signer, no certs? */ andre@0: return SECFailure; /* XXX set an error? */ andre@0: andre@0: if (certdb == NULL) { andre@0: certdb = CERT_GetDefaultCertDB(); andre@0: if (certdb == NULL) { andre@0: PORT_SetError (SEC_ERROR_BAD_DATABASE); andre@0: return SECFailure; andre@0: } andre@0: } andre@0: andre@0: /* XXX Should it be an error if we find no signerinfo or no certs? */ andre@0: while ((signerinfo = *signerinfos++) != NULL) { andre@0: if (signerinfo->cert != NULL) andre@0: /* get the cert chain. don't send the root to avoid contamination andre@0: * of old clients with a new root that they don't trust andre@0: */ andre@0: signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert, andre@0: certUsageEmailSigner, andre@0: PR_FALSE); andre@0: } andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Helper function to add a certificate chain for inclusion in the andre@0: * bag of certificates in a signedData. andre@0: */ andre@0: static SECStatus andre@0: sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate *cert, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: SECOidTag kind; andre@0: CERTCertificateList *certlist, **certlists, ***certlistsp; andre@0: int count; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: switch (kind) { andre@0: case SEC_OID_PKCS7_SIGNED_DATA: andre@0: { andre@0: SEC_PKCS7SignedData *sdp; andre@0: andre@0: sdp = cinfo->content.signedData; andre@0: certlistsp = &(sdp->certLists); andre@0: } andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: { andre@0: SEC_PKCS7SignedAndEnvelopedData *saedp; andre@0: andre@0: saedp = cinfo->content.signedAndEnvelopedData; andre@0: certlistsp = &(saedp->certLists); andre@0: } andre@0: break; andre@0: default: andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: if (certdb == NULL) { andre@0: certdb = CERT_GetDefaultCertDB(); andre@0: if (certdb == NULL) { andre@0: PORT_SetError (SEC_ERROR_BAD_DATABASE); andre@0: return SECFailure; andre@0: } andre@0: } andre@0: andre@0: certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE); andre@0: if (certlist == NULL) andre@0: return SECFailure; andre@0: andre@0: certlists = *certlistsp; andre@0: if (certlists == NULL) { andre@0: count = 0; andre@0: certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp, andre@0: 2 * sizeof(CERTCertificateList *)); andre@0: } else { andre@0: for (count = 0; certlists[count] != NULL; count++) andre@0: ; andre@0: PORT_Assert (count); /* should be at least one already */ andre@0: certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, andre@0: certlists, andre@0: (count + 1) * sizeof(CERTCertificateList *), andre@0: (count + 2) * sizeof(CERTCertificateList *)); andre@0: } andre@0: andre@0: if (certlists == NULL) { andre@0: CERT_DestroyCertificateList (certlist); andre@0: return SECFailure; andre@0: } andre@0: andre@0: certlists[count] = certlist; andre@0: certlists[count + 1] = NULL; andre@0: andre@0: *certlistsp = certlists; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Helper function to add a certificate for inclusion in the bag of andre@0: * certificates in a signedData. andre@0: */ andre@0: static SECStatus andre@0: sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate *cert) andre@0: { andre@0: SECOidTag kind; andre@0: CERTCertificate **certs, ***certsp; andre@0: int count; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: switch (kind) { andre@0: case SEC_OID_PKCS7_SIGNED_DATA: andre@0: { andre@0: SEC_PKCS7SignedData *sdp; andre@0: andre@0: sdp = cinfo->content.signedData; andre@0: certsp = &(sdp->certs); andre@0: } andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: { andre@0: SEC_PKCS7SignedAndEnvelopedData *saedp; andre@0: andre@0: saedp = cinfo->content.signedAndEnvelopedData; andre@0: certsp = &(saedp->certs); andre@0: } andre@0: break; andre@0: default: andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: cert = CERT_DupCertificate (cert); andre@0: if (cert == NULL) andre@0: return SECFailure; andre@0: andre@0: certs = *certsp; andre@0: if (certs == NULL) { andre@0: count = 0; andre@0: certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, andre@0: 2 * sizeof(CERTCertificate *)); andre@0: } else { andre@0: for (count = 0; certs[count] != NULL; count++) andre@0: ; andre@0: PORT_Assert (count); /* should be at least one already */ andre@0: certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs, andre@0: (count + 1) * sizeof(CERTCertificate *), andre@0: (count + 2) * sizeof(CERTCertificate *)); andre@0: } andre@0: andre@0: if (certs == NULL) { andre@0: CERT_DestroyCertificate (cert); andre@0: return SECFailure; andre@0: } andre@0: andre@0: certs[count] = cert; andre@0: certs[count + 1] = NULL; andre@0: andre@0: *certsp = certs; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Create a PKCS7 certs-only container. andre@0: * andre@0: * "cert" is the (first) cert that will be included. andre@0: * andre@0: * "include_chain" specifies whether the entire chain for "cert" should andre@0: * be included. andre@0: * andre@0: * "certdb" is the cert database to use for finding the chain. andre@0: * It can be NULL in when "include_chain" is false, or when meaning andre@0: * use the default database. andre@0: * andre@0: * More certs and chains can be added via AddCertificate and AddCertChain. andre@0: * andre@0: * An error results in a return value of NULL and an error set. andre@0: * (Retrieve specific errors via PORT_GetError()/XP_GetError().) andre@0: */ andre@0: SEC_PKCS7ContentInfo * andre@0: SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, andre@0: PRBool include_chain, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: SECStatus rv; andre@0: andre@0: cinfo = sec_pkcs7_create_signed_data (NULL, NULL); andre@0: if (cinfo == NULL) andre@0: return NULL; andre@0: andre@0: if (include_chain) andre@0: rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb); andre@0: else andre@0: rv = sec_pkcs7_add_certificate (cinfo, cert); andre@0: andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: return cinfo; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add "cert" and its entire chain to the set of certs included in "cinfo". andre@0: * andre@0: * "certdb" is the cert database to use for finding the chain. andre@0: * It can be NULL, meaning use the default database. andre@0: * andre@0: * "cinfo" should be of type signedData or signedAndEnvelopedData; andre@0: * SECFailure will be returned if it is not. andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate *cert, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: SECOidTag kind; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: if (kind != SEC_OID_PKCS7_SIGNED_DATA andre@0: && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) andre@0: return SECFailure; /* XXX set an error? */ andre@0: andre@0: return sec_pkcs7_add_cert_chain (cinfo, cert, certdb); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add "cert" to the set of certs included in "cinfo". andre@0: * andre@0: * "cinfo" should be of type signedData or signedAndEnvelopedData; andre@0: * SECFailure will be returned if it is not. andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert) andre@0: { andre@0: SECOidTag kind; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: if (kind != SEC_OID_PKCS7_SIGNED_DATA andre@0: && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) andre@0: return SECFailure; /* XXX set an error? */ andre@0: andre@0: return sec_pkcs7_add_certificate (cinfo, cert); andre@0: } andre@0: andre@0: andre@0: static SECStatus andre@0: sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo, andre@0: PLArenaPool *poolp, andre@0: SECOidTag kind, PRBool detached, andre@0: SECOidTag encalg, int keysize) andre@0: { andre@0: SECStatus rv; andre@0: andre@0: PORT_Assert (enccinfo != NULL && poolp != NULL); andre@0: if (enccinfo == NULL || poolp == NULL) andre@0: return SECFailure; andre@0: andre@0: /* andre@0: * XXX Some day we may want to allow for other kinds. That needs andre@0: * more work and modifications to the creation interface, etc. andre@0: * For now, allow but notice callers who pass in other kinds. andre@0: * They are responsible for creating the inner type and encoding, andre@0: * if it is other than DATA. andre@0: */ andre@0: PORT_Assert (kind == SEC_OID_PKCS7_DATA); andre@0: andre@0: enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind); andre@0: PORT_Assert (enccinfo->contentTypeTag andre@0: && enccinfo->contentTypeTag->offset == kind); andre@0: andre@0: rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType), andre@0: &(enccinfo->contentTypeTag->oid)); andre@0: if (rv != SECSuccess) andre@0: return rv; andre@0: andre@0: /* Save keysize and algorithm for later. */ andre@0: enccinfo->keysize = keysize; andre@0: enccinfo->encalg = encalg; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add a recipient to a PKCS7 thing, verifying their cert first. andre@0: * Any error returns SECFailure. andre@0: */ andre@0: static SECStatus andre@0: sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate *cert, andre@0: SECCertUsage certusage, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: SECOidTag kind; andre@0: SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp; andre@0: SECItem *dummy; andre@0: void *mark; andre@0: int count; andre@0: andre@0: kind = SEC_PKCS7ContentType (cinfo); andre@0: switch (kind) { andre@0: case SEC_OID_PKCS7_ENVELOPED_DATA: andre@0: { andre@0: SEC_PKCS7EnvelopedData *edp; andre@0: andre@0: edp = cinfo->content.envelopedData; andre@0: recipientinfosp = &(edp->recipientInfos); andre@0: } andre@0: break; andre@0: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: andre@0: { andre@0: SEC_PKCS7SignedAndEnvelopedData *saedp; andre@0: andre@0: saedp = cinfo->content.signedAndEnvelopedData; andre@0: recipientinfosp = &(saedp->recipientInfos); andre@0: } andre@0: break; andre@0: default: andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: /* andre@0: * XXX I think that CERT_VerifyCert should do this if *it* is passed andre@0: * a NULL database. andre@0: */ andre@0: if (certdb == NULL) { andre@0: certdb = CERT_GetDefaultCertDB(); andre@0: if (certdb == NULL) andre@0: return SECFailure; /* XXX set an error? */ andre@0: } andre@0: andre@0: if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), andre@0: cinfo->pwfn_arg, NULL) != SECSuccess) andre@0: { andre@0: /* XXX Did CERT_VerifyCert set an error? */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: mark = PORT_ArenaMark (cinfo->poolp); andre@0: andre@0: recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp, andre@0: sizeof(SEC_PKCS7RecipientInfo)); andre@0: if (recipientinfo == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version, andre@0: SEC_PKCS7_RECIPIENT_INFO_VERSION); andre@0: if (dummy == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: PORT_Assert (dummy == &recipientinfo->version); andre@0: andre@0: recipientinfo->cert = CERT_DupCertificate (cert); andre@0: if (recipientinfo->cert == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); andre@0: if (recipientinfo->issuerAndSN == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * Okay, now recipientinfo is all set. We just need to put it into andre@0: * the main structure. andre@0: * andre@0: * If this is the first recipient, allocate a new recipientinfos array; andre@0: * otherwise, reallocate the array, making room for the new entry. andre@0: */ andre@0: recipientinfos = *recipientinfosp; andre@0: if (recipientinfos == NULL) { andre@0: count = 0; andre@0: recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc ( andre@0: cinfo->poolp, andre@0: 2 * sizeof(SEC_PKCS7RecipientInfo *)); andre@0: } else { andre@0: for (count = 0; recipientinfos[count] != NULL; count++) andre@0: ; andre@0: PORT_Assert (count); /* should be at least one already */ andre@0: recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow ( andre@0: cinfo->poolp, recipientinfos, andre@0: (count + 1) * sizeof(SEC_PKCS7RecipientInfo *), andre@0: (count + 2) * sizeof(SEC_PKCS7RecipientInfo *)); andre@0: } andre@0: andre@0: if (recipientinfos == NULL) { andre@0: PORT_ArenaRelease (cinfo->poolp, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: recipientinfos[count] = recipientinfo; andre@0: recipientinfos[count + 1] = NULL; andre@0: andre@0: *recipientinfosp = recipientinfos; andre@0: andre@0: PORT_ArenaUnmark (cinfo->poolp, mark); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Start a PKCS7 enveloping context. andre@0: * andre@0: * "cert" is the cert for the recipient. It will be checked for validity. andre@0: * andre@0: * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) andre@0: * XXX Maybe SECCertUsage should be split so that our caller just says andre@0: * "email" and *we* add the "recipient" part -- otherwise our caller andre@0: * could be lying about the usage; we do not want to allow encryption andre@0: * certs for signing or vice versa. andre@0: * andre@0: * "certdb" is the cert database to use for verifying the cert. andre@0: * It can be NULL if a default database is available (like in the client). andre@0: * andre@0: * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). andre@0: * andre@0: * "keysize" specifies the bulk encryption key size, in bits. andre@0: * andre@0: * The return value can be passed to functions which add things to andre@0: * it like more recipients, then eventually to SEC_PKCS7Encode() or to andre@0: * SEC_PKCS7EncoderStart() to create the encoded data, and finally to andre@0: * SEC_PKCS7DestroyContentInfo(). andre@0: * andre@0: * An error results in a return value of NULL and an error set. andre@0: * (Retrieve specific errors via PORT_GetError()/XP_GetError().) andre@0: */ andre@0: extern SEC_PKCS7ContentInfo * andre@0: SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, andre@0: SECCertUsage certusage, andre@0: CERTCertDBHandle *certdb, andre@0: SECOidTag encalg, andre@0: int keysize, andre@0: SECKEYGetPasswordKey pwfn, void *pwfn_arg) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: SEC_PKCS7EnvelopedData *envd; andre@0: SECStatus rv; andre@0: andre@0: cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA, andre@0: PR_FALSE, pwfn, pwfn_arg); andre@0: if (cinfo == NULL) andre@0: return NULL; andre@0: andre@0: rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: envd = cinfo->content.envelopedData; andre@0: PORT_Assert (envd != NULL); andre@0: andre@0: /* andre@0: * XXX Might we want to allow content types other than data? andre@0: * If so, via what interface? andre@0: */ andre@0: rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo), andre@0: cinfo->poolp, andre@0: SEC_OID_PKCS7_DATA, PR_FALSE, andre@0: encalg, keysize); andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: /* XXX Anything more to do here? */ andre@0: andre@0: return cinfo; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Add another recipient to an encrypted message. andre@0: * andre@0: * "cinfo" should be of type envelopedData or signedAndEnvelopedData; andre@0: * SECFailure will be returned if it is not. andre@0: * andre@0: * "cert" is the cert for the recipient. It will be checked for validity. andre@0: * andre@0: * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) andre@0: * XXX Maybe SECCertUsage should be split so that our caller just says andre@0: * "email" and *we* add the "recipient" part -- otherwise our caller andre@0: * could be lying about the usage; we do not want to allow encryption andre@0: * certs for signing or vice versa. andre@0: * andre@0: * "certdb" is the cert database to use for verifying the cert. andre@0: * It can be NULL if a default database is available (like in the client). andre@0: */ andre@0: SECStatus andre@0: SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, andre@0: CERTCertificate *cert, andre@0: SECCertUsage certusage, andre@0: CERTCertDBHandle *certdb) andre@0: { andre@0: return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Create an empty PKCS7 data content info. andre@0: * andre@0: * An error results in a return value of NULL and an error set. andre@0: * (Retrieve specific errors via PORT_GetError()/XP_GetError().) andre@0: */ andre@0: SEC_PKCS7ContentInfo * andre@0: SEC_PKCS7CreateData (void) andre@0: { andre@0: return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE, andre@0: NULL, NULL); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Create an empty PKCS7 encrypted content info. andre@0: * andre@0: * "algorithm" specifies the bulk encryption algorithm to use. andre@0: * andre@0: * An error results in a return value of NULL and an error set. andre@0: * (Retrieve specific errors via PORT_GetError()/XP_GetError().) andre@0: */ andre@0: SEC_PKCS7ContentInfo * andre@0: SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, andre@0: SECKEYGetPasswordKey pwfn, void *pwfn_arg) andre@0: { andre@0: SEC_PKCS7ContentInfo *cinfo; andre@0: SECAlgorithmID *algid; andre@0: SEC_PKCS7EncryptedData *enc_data; andre@0: SECStatus rv; andre@0: andre@0: cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, andre@0: PR_FALSE, pwfn, pwfn_arg); andre@0: if (cinfo == NULL) andre@0: return NULL; andre@0: andre@0: enc_data = cinfo->content.encryptedData; andre@0: algid = &(enc_data->encContentInfo.contentEncAlg); andre@0: andre@0: if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { andre@0: rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); andre@0: } else { andre@0: /* Assume password-based-encryption. andre@0: * Note: we can't generate pkcs5v2 from this interface. andre@0: * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting andre@0: * non-PBE oids and assuming that they are pkcs5v2 oids, but andre@0: * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular andre@0: * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData andre@0: * to create pkcs5v2 PBEs */ andre@0: SECAlgorithmID *pbe_algid; andre@0: pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, andre@0: NSS_PBE_DEFAULT_ITERATION_COUNT, andre@0: NULL); andre@0: if (pbe_algid == NULL) { andre@0: rv = SECFailure; andre@0: } else { andre@0: rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); andre@0: SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); andre@0: } andre@0: } andre@0: andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo), andre@0: cinfo->poolp, andre@0: SEC_OID_PKCS7_DATA, PR_FALSE, andre@0: algorithm, keysize); andre@0: if (rv != SECSuccess) { andre@0: SEC_PKCS7DestroyContentInfo (cinfo); andre@0: return NULL; andre@0: } andre@0: andre@0: return cinfo; andre@0: } andre@0: