diff options
author | cvs2hg <devnull@localhost> | 2004-01-07 13:36:20 +0000 |
---|---|---|
committer | cvs2hg <devnull@localhost> | 2004-01-07 13:36:20 +0000 |
commit | cdcbe4d57affae2f0db0bb0eeaea3fddc2f28afe (patch) | |
tree | 60f187758cdcdb3e315b5d9b7730e9310b00e5ff /security/nss/lib/pkcs7/p7create.c | |
parent | 550b26306b0ab09ebb342981c9ccedd7d9c190ff (diff) | |
download | nss-hg-cdcbe4d57affae2f0db0bb0eeaea3fddc2f28afe.tar.gz |
fixup commit for tag 'PACKAGING_20030906_BASE'PACKAGING_20030906_BASE
Diffstat (limited to 'security/nss/lib/pkcs7/p7create.c')
-rw-r--r-- | security/nss/lib/pkcs7/p7create.c | 1320 |
1 files changed, 0 insertions, 1320 deletions
diff --git a/security/nss/lib/pkcs7/p7create.c b/security/nss/lib/pkcs7/p7create.c deleted file mode 100644 index 6454db74c..000000000 --- a/security/nss/lib/pkcs7/p7create.c +++ /dev/null @@ -1,1320 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape security libraries. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * PKCS7 creation. - * - * $Id$ - */ - -#include "p7local.h" - -#include "cert.h" -#include "secasn1.h" -#include "secitem.h" -#include "secoid.h" -#include "pk11func.h" -#include "prtime.h" -#include "secerr.h" -#include "secder.h" - -static SECStatus -sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp, - SECOidTag kind, PRBool detached) -{ - void *thing; - int version; - SECItem *versionp; - SECStatus rv; - - PORT_Assert (cinfo != NULL && poolp != NULL); - if (cinfo == NULL || poolp == NULL) - return SECFailure; - - cinfo->contentTypeTag = SECOID_FindOIDByTag (kind); - PORT_Assert (cinfo->contentTypeTag - && cinfo->contentTypeTag->offset == kind); - - rv = SECITEM_CopyItem (poolp, &(cinfo->contentType), - &(cinfo->contentTypeTag->oid)); - if (rv != SECSuccess) - return rv; - - if (detached) - return SECSuccess; - - switch (kind) { - default: - case SEC_OID_PKCS7_DATA: - thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem)); - cinfo->content.data = (SECItem*)thing; - versionp = NULL; - version = -1; - break; - case SEC_OID_PKCS7_DIGESTED_DATA: - thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData)); - cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing; - versionp = &(cinfo->content.digestedData->version); - version = SEC_PKCS7_DIGESTED_DATA_VERSION; - break; - case SEC_OID_PKCS7_ENCRYPTED_DATA: - thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData)); - cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing; - versionp = &(cinfo->content.encryptedData->version); - version = SEC_PKCS7_ENCRYPTED_DATA_VERSION; - break; - case SEC_OID_PKCS7_ENVELOPED_DATA: - thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData)); - cinfo->content.envelopedData = - (SEC_PKCS7EnvelopedData*)thing; - versionp = &(cinfo->content.envelopedData->version); - version = SEC_PKCS7_ENVELOPED_DATA_VERSION; - break; - case SEC_OID_PKCS7_SIGNED_DATA: - thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData)); - cinfo->content.signedData = - (SEC_PKCS7SignedData*)thing; - versionp = &(cinfo->content.signedData->version); - version = SEC_PKCS7_SIGNED_DATA_VERSION; - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData)); - cinfo->content.signedAndEnvelopedData = - (SEC_PKCS7SignedAndEnvelopedData*)thing; - versionp = &(cinfo->content.signedAndEnvelopedData->version); - version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION; - break; - } - - if (thing == NULL) - return SECFailure; - - if (versionp != NULL) { - SECItem *dummy; - - PORT_Assert (version >= 0); - dummy = SEC_ASN1EncodeInteger (poolp, versionp, version); - if (dummy == NULL) - return SECFailure; - PORT_Assert (dummy == versionp); - } - - return SECSuccess; -} - - -static SEC_PKCS7ContentInfo * -sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached, - SECKEYGetPasswordKey pwfn, void *pwfn_arg) -{ - SEC_PKCS7ContentInfo *cinfo; - PRArenaPool *poolp; - SECStatus rv; - - poolp = PORT_NewArena (1024); /* XXX what is right value? */ - if (poolp == NULL) - return NULL; - - cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); - if (cinfo == NULL) { - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - cinfo->poolp = poolp; - cinfo->pwfn = pwfn; - cinfo->pwfn_arg = pwfn_arg; - cinfo->created = PR_TRUE; - cinfo->refCount = 1; - - rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached); - if (rv != SECSuccess) { - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - return cinfo; -} - - -/* - * Add a signer to a PKCS7 thing, verifying the signature cert first. - * Any error returns SECFailure. - * - * XXX Right now this only adds the *first* signer. It fails if you try - * to add a second one -- this needs to be fixed. - */ -static SECStatus -sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate * cert, - SECCertUsage certusage, - CERTCertDBHandle * certdb, - SECOidTag digestalgtag, - SECItem * digestdata) -{ - SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp; - SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp; - SECItem *digest, **digests, ***digestsp; - SECItem * dummy; - void * mark; - SECStatus rv; - SECOidTag kind; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - case SEC_OID_PKCS7_SIGNED_DATA: - { - SEC_PKCS7SignedData *sdp; - - sdp = cinfo->content.signedData; - digestalgsp = &(sdp->digestAlgorithms); - digestsp = &(sdp->digests); - signerinfosp = &(sdp->signerInfos); - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - digestalgsp = &(saedp->digestAlgorithms); - digestsp = &(saedp->digests); - signerinfosp = &(saedp->signerInfos); - } - break; - default: - return SECFailure; /* XXX set an error? */ - } - - /* - * XXX I think that CERT_VerifyCert should do this if *it* is passed - * a NULL database. - */ - if (certdb == NULL) { - certdb = CERT_GetDefaultCertDB(); - if (certdb == NULL) - return SECFailure; /* XXX set an error? */ - } - - if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), - cinfo->pwfn_arg, NULL) != SECSuccess) - { - /* XXX Did CERT_VerifyCert set an error? */ - return SECFailure; - } - - /* - * XXX This is the check that we do not already have a signer. - * This is not what we really want -- we want to allow this - * and *add* the new signer. - */ - PORT_Assert (*signerinfosp == NULL - && *digestalgsp == NULL && *digestsp == NULL); - if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL) - return SECFailure; - - mark = PORT_ArenaMark (cinfo->poolp); - - signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, - sizeof(SEC_PKCS7SignerInfo)); - if (signerinfo == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version, - SEC_PKCS7_SIGNER_INFO_VERSION); - if (dummy == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - PORT_Assert (dummy == &signerinfo->version); - - signerinfo->cert = CERT_DupCertificate (cert); - if (signerinfo->cert == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); - if (signerinfo->issuerAndSN == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg, - digestalgtag, NULL); - if (rv != SECSuccess) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - /* - * Okay, now signerinfo is all set. We just need to put it and its - * companions (another copy of the digest algorithm, and the digest - * itself if given) into the main structure. - * - * XXX If we are handling more than one signer, the following code - * needs to look through the digest algorithms already specified - * and see if the same one is there already. If it is, it does not - * need to be added again. Also, if it is there *and* the digest - * is not null, then the digest given should match the digest already - * specified -- if not, that is an error. Finally, the new signerinfo - * should be *added* to the set already found. - */ - - signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp, - 2 * sizeof(SEC_PKCS7SignerInfo *)); - if (signerinfos == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - signerinfos[0] = signerinfo; - signerinfos[1] = NULL; - - digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID)); - digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *)); - if (digestalg == NULL || digestalgs == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL); - if (rv != SECSuccess) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - digestalgs[0] = digestalg; - digestalgs[1] = NULL; - - if (digestdata != NULL) { - digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem)); - digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, - 2 * sizeof(SECItem *)); - if (digest == NULL || digests == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata); - if (rv != SECSuccess) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - digests[0] = digest; - digests[1] = NULL; - } else { - digests = NULL; - } - - *signerinfosp = signerinfos; - *digestalgsp = digestalgs; - *digestsp = digests; - - PORT_ArenaUnmark(cinfo->poolp, mark); - return SECSuccess; -} - - -/* - * Helper function for creating an empty signedData. - */ -static SEC_PKCS7ContentInfo * -sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg) -{ - SEC_PKCS7ContentInfo *cinfo; - SEC_PKCS7SignedData *sigd; - SECStatus rv; - - cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE, - pwfn, pwfn_arg); - if (cinfo == NULL) - return NULL; - - sigd = cinfo->content.signedData; - PORT_Assert (sigd != NULL); - - /* - * XXX Might we want to allow content types other than data? - * If so, via what interface? - */ - rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp, - SEC_OID_PKCS7_DATA, PR_TRUE); - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - return cinfo; -} - - -/* - * Start a PKCS7 signing context. - * - * "cert" is the cert that will be used to sign the data. It will be - * checked for validity. - * - * "certusage" describes the signing usage (e.g. certUsageEmailSigner) - * XXX Maybe SECCertUsage should be split so that our caller just says - * "email" and *we* add the "signing" part -- otherwise our caller - * could be lying about the usage; we do not want to allow encryption - * certs for signing or vice versa. - * - * "certdb" is the cert database to use for verifying the cert. - * It can be NULL if a default database is available (like in the client). - * - * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). - * - * "digest" is the actual digest of the data. It must be provided in - * the case of detached data or NULL if the content will be included. - * - * The return value can be passed to functions which add things to - * it like attributes, then eventually to SEC_PKCS7Encode() or to - * SEC_PKCS7EncoderStart() to create the encoded data, and finally to - * SEC_PKCS7DestroyContentInfo(). - * - * An error results in a return value of NULL and an error set. - * (Retrieve specific errors via PORT_GetError()/XP_GetError().) - */ -SEC_PKCS7ContentInfo * -SEC_PKCS7CreateSignedData (CERTCertificate *cert, - SECCertUsage certusage, - CERTCertDBHandle *certdb, - SECOidTag digestalg, - SECItem *digest, - SECKEYGetPasswordKey pwfn, void *pwfn_arg) -{ - SEC_PKCS7ContentInfo *cinfo; - SECStatus rv; - - cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg); - if (cinfo == NULL) - return NULL; - - rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb, - digestalg, digest); - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - return cinfo; -} - - -static SEC_PKCS7Attribute * -sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag, - SECItem *value, PRBool encoded) -{ - SEC_PKCS7Attribute *attr; - SECItem **values; - void *mark; - - PORT_Assert (poolp != NULL); - mark = PORT_ArenaMark (poolp); - - attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, - sizeof(SEC_PKCS7Attribute)); - if (attr == NULL) - goto loser; - - attr->typeTag = SECOID_FindOIDByTag (oidtag); - if (attr->typeTag == NULL) - goto loser; - - if (SECITEM_CopyItem (poolp, &(attr->type), - &(attr->typeTag->oid)) != SECSuccess) - goto loser; - - values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *)); - if (values == NULL) - goto loser; - - if (value != NULL) { - SECItem *copy; - - copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); - if (copy == NULL) - goto loser; - - if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess) - goto loser; - - value = copy; - } - - values[0] = value; - values[1] = NULL; - attr->values = values; - attr->encoded = encoded; - - PORT_ArenaUnmark (poolp, mark); - return attr; - -loser: - PORT_Assert (mark != NULL); - PORT_ArenaRelease (poolp, mark); - return NULL; -} - - -static SECStatus -sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo, - SEC_PKCS7Attribute ***attrsp, - SEC_PKCS7Attribute *attr) -{ - SEC_PKCS7Attribute **attrs; - SECItem *ct_value; - void *mark; - - PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); - if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) - return SECFailure; - - attrs = *attrsp; - if (attrs != NULL) { - int count; - - /* - * We already have some attributes, and just need to add this - * new one. - */ - - /* - * We should already have the *required* attributes, which were - * created/added at the same time the first attribute was added. - */ - PORT_Assert (sec_PKCS7FindAttribute (attrs, - SEC_OID_PKCS9_CONTENT_TYPE, - PR_FALSE) != NULL); - PORT_Assert (sec_PKCS7FindAttribute (attrs, - SEC_OID_PKCS9_MESSAGE_DIGEST, - PR_FALSE) != NULL); - - for (count = 0; attrs[count] != NULL; count++) - ; - attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs, - (count + 1) * sizeof(SEC_PKCS7Attribute *), - (count + 2) * sizeof(SEC_PKCS7Attribute *)); - if (attrs == NULL) - return SECFailure; - - attrs[count] = attr; - attrs[count+1] = NULL; - *attrsp = attrs; - - return SECSuccess; - } - - /* - * This is the first time an attribute is going in. - * We need to create and add the required attributes, and then - * we will also add in the one our caller gave us. - */ - - /* - * There are 2 required attributes, plus the one our caller wants - * to add, plus we always end with a NULL one. Thus, four slots. - */ - attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, - 4 * sizeof(SEC_PKCS7Attribute *)); - if (attrs == NULL) - return SECFailure; - - mark = PORT_ArenaMark (cinfo->poolp); - - /* - * First required attribute is the content type of the data - * being signed. - */ - ct_value = &(cinfo->content.signedData->contentInfo.contentType); - attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp, - SEC_OID_PKCS9_CONTENT_TYPE, - ct_value, PR_FALSE); - /* - * Second required attribute is the message digest of the data - * being signed; we leave the value NULL for now (just create - * the place for it to go), and the encoder will fill it in later. - */ - attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp, - SEC_OID_PKCS9_MESSAGE_DIGEST, - NULL, PR_FALSE); - if (attrs[0] == NULL || attrs[1] == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - attrs[2] = attr; - attrs[3] = NULL; - *attrsp = attrs; - - PORT_ArenaUnmark (cinfo->poolp, mark); - return SECSuccess; -} - - -/* - * Add the signing time to the authenticated (i.e. signed) attributes - * of "cinfo". This is expected to be included in outgoing signed - * messages for email (S/MIME) but is likely useful in other situations. - * - * This should only be added once; a second call will either do - * nothing or replace an old signing time with a newer one. - * - * XXX This will probably just shove the current time into "cinfo" - * but it will not actually get signed until the entire item is - * processed for encoding. Is this (expected to be small) delay okay? - * - * "cinfo" should be of type signedData (the only kind of pkcs7 data - * that is allowed authenticated attributes); SECFailure will be returned - * if it is not. - */ -SECStatus -SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo) -{ - SEC_PKCS7SignerInfo **signerinfos; - SEC_PKCS7Attribute *attr; - SECItem stime; - SECStatus rv; - int si; - - PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); - if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) - return SECFailure; - - signerinfos = cinfo->content.signedData->signerInfos; - - /* There has to be a signer, or it makes no sense. */ - if (signerinfos == NULL || signerinfos[0] == NULL) - return SECFailure; - - rv = CERT_EncodeTimeChoice(NULL, &stime, PR_Now()); - if (rv != SECSuccess) - return rv; - - attr = sec_pkcs7_create_attribute (cinfo->poolp, - SEC_OID_PKCS9_SIGNING_TIME, - &stime, PR_FALSE); - SECITEM_FreeItem (&stime, PR_FALSE); - - if (attr == NULL) - return SECFailure; - - rv = SECSuccess; - for (si = 0; signerinfos[si] != NULL; si++) { - SEC_PKCS7Attribute *oattr; - - oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr, - SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE); - PORT_Assert (oattr == NULL); - if (oattr != NULL) - continue; /* XXX or would it be better to replace it? */ - - rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr), - attr); - if (rv != SECSuccess) - break; /* could try to continue, but may as well give up now */ - } - - return rv; -} - - -/* - * Add the specified attribute to the authenticated (i.e. signed) attributes - * of "cinfo" -- "oidtag" describes the attribute and "value" is the - * value to be associated with it. NOTE! "value" must already be encoded; - * no interpretation of "oidtag" is done. Also, it is assumed that this - * signedData has only one signer -- if we ever need to add attributes - * when there is more than one signature, we need a way to specify *which* - * signature should get the attribute. - * - * XXX Technically, a signed attribute can have multiple values; if/when - * we ever need to support an attribute which takes multiple values, we - * either need to change this interface or create an AddSignedAttributeValue - * which can be called subsequently, and would then append a value. - * - * "cinfo" should be of type signedData (the only kind of pkcs7 data - * that is allowed authenticated attributes); SECFailure will be returned - * if it is not. - */ -SECStatus -SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, - SECOidTag oidtag, - SECItem *value) -{ - SEC_PKCS7SignerInfo **signerinfos; - SEC_PKCS7Attribute *attr; - - PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); - if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) - return SECFailure; - - signerinfos = cinfo->content.signedData->signerInfos; - - /* - * No signature or more than one means no deal. - */ - if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) - return SECFailure; - - attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE); - if (attr == NULL) - return SECFailure; - - return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr); -} - - -/* - * Mark that the signer certificates and their issuing chain should - * be included in the encoded data. This is expected to be used - * in outgoing signed messages for email (S/MIME). - * - * "certdb" is the cert database to use for finding the chain. - * It can be NULL, meaning use the default database. - * - * "cinfo" should be of type signedData or signedAndEnvelopedData; - * SECFailure will be returned if it is not. - */ -SECStatus -SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, - CERTCertDBHandle *certdb) -{ - SECOidTag kind; - SEC_PKCS7SignerInfo *signerinfo, **signerinfos; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - case SEC_OID_PKCS7_SIGNED_DATA: - signerinfos = cinfo->content.signedData->signerInfos; - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; - break; - default: - return SECFailure; /* XXX set an error? */ - } - - if (signerinfos == NULL) /* no signer, no certs? */ - return SECFailure; /* XXX set an error? */ - - if (certdb == NULL) { - certdb = CERT_GetDefaultCertDB(); - if (certdb == NULL) { - PORT_SetError (SEC_ERROR_BAD_DATABASE); - return SECFailure; - } - } - - /* XXX Should it be an error if we find no signerinfo or no certs? */ - while ((signerinfo = *signerinfos++) != NULL) { - if (signerinfo->cert != NULL) - /* get the cert chain. don't send the root to avoid contamination - * of old clients with a new root that they don't trust - */ - signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert, - certUsageEmailSigner, - PR_FALSE); - } - - return SECSuccess; -} - - -/* - * Helper function to add a certificate chain for inclusion in the - * bag of certificates in a signedData. - */ -static SECStatus -sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate *cert, - CERTCertDBHandle *certdb) -{ - SECOidTag kind; - CERTCertificateList *certlist, **certlists, ***certlistsp; - int count; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - case SEC_OID_PKCS7_SIGNED_DATA: - { - SEC_PKCS7SignedData *sdp; - - sdp = cinfo->content.signedData; - certlistsp = &(sdp->certLists); - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - certlistsp = &(saedp->certLists); - } - break; - default: - return SECFailure; /* XXX set an error? */ - } - - if (certdb == NULL) { - certdb = CERT_GetDefaultCertDB(); - if (certdb == NULL) { - PORT_SetError (SEC_ERROR_BAD_DATABASE); - return SECFailure; - } - } - - certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE); - if (certlist == NULL) - return SECFailure; - - certlists = *certlistsp; - if (certlists == NULL) { - count = 0; - certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp, - 2 * sizeof(CERTCertificateList *)); - } else { - for (count = 0; certlists[count] != NULL; count++) - ; - PORT_Assert (count); /* should be at least one already */ - certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, - certlists, - (count + 1) * sizeof(CERTCertificateList *), - (count + 2) * sizeof(CERTCertificateList *)); - } - - if (certlists == NULL) { - CERT_DestroyCertificateList (certlist); - return SECFailure; - } - - certlists[count] = certlist; - certlists[count + 1] = NULL; - - *certlistsp = certlists; - - return SECSuccess; -} - - -/* - * Helper function to add a certificate for inclusion in the bag of - * certificates in a signedData. - */ -static SECStatus -sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate *cert) -{ - SECOidTag kind; - CERTCertificate **certs, ***certsp; - int count; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - case SEC_OID_PKCS7_SIGNED_DATA: - { - SEC_PKCS7SignedData *sdp; - - sdp = cinfo->content.signedData; - certsp = &(sdp->certs); - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - certsp = &(saedp->certs); - } - break; - default: - return SECFailure; /* XXX set an error? */ - } - - cert = CERT_DupCertificate (cert); - if (cert == NULL) - return SECFailure; - - certs = *certsp; - if (certs == NULL) { - count = 0; - certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, - 2 * sizeof(CERTCertificate *)); - } else { - for (count = 0; certs[count] != NULL; count++) - ; - PORT_Assert (count); /* should be at least one already */ - certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs, - (count + 1) * sizeof(CERTCertificate *), - (count + 2) * sizeof(CERTCertificate *)); - } - - if (certs == NULL) { - CERT_DestroyCertificate (cert); - return SECFailure; - } - - certs[count] = cert; - certs[count + 1] = NULL; - - *certsp = certs; - - return SECSuccess; -} - - -/* - * Create a PKCS7 certs-only container. - * - * "cert" is the (first) cert that will be included. - * - * "include_chain" specifies whether the entire chain for "cert" should - * be included. - * - * "certdb" is the cert database to use for finding the chain. - * It can be NULL in when "include_chain" is false, or when meaning - * use the default database. - * - * More certs and chains can be added via AddCertificate and AddCertChain. - * - * An error results in a return value of NULL and an error set. - * (Retrieve specific errors via PORT_GetError()/XP_GetError().) - */ -SEC_PKCS7ContentInfo * -SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, - PRBool include_chain, - CERTCertDBHandle *certdb) -{ - SEC_PKCS7ContentInfo *cinfo; - SECStatus rv; - - cinfo = sec_pkcs7_create_signed_data (NULL, NULL); - if (cinfo == NULL) - return NULL; - - if (include_chain) - rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb); - else - rv = sec_pkcs7_add_certificate (cinfo, cert); - - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - return cinfo; -} - - -/* - * Add "cert" and its entire chain to the set of certs included in "cinfo". - * - * "certdb" is the cert database to use for finding the chain. - * It can be NULL, meaning use the default database. - * - * "cinfo" should be of type signedData or signedAndEnvelopedData; - * SECFailure will be returned if it is not. - */ -SECStatus -SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate *cert, - CERTCertDBHandle *certdb) -{ - SECOidTag kind; - - kind = SEC_PKCS7ContentType (cinfo); - if (kind != SEC_OID_PKCS7_SIGNED_DATA - && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) - return SECFailure; /* XXX set an error? */ - - return sec_pkcs7_add_cert_chain (cinfo, cert, certdb); -} - - -/* - * Add "cert" to the set of certs included in "cinfo". - * - * "cinfo" should be of type signedData or signedAndEnvelopedData; - * SECFailure will be returned if it is not. - */ -SECStatus -SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert) -{ - SECOidTag kind; - - kind = SEC_PKCS7ContentType (cinfo); - if (kind != SEC_OID_PKCS7_SIGNED_DATA - && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) - return SECFailure; /* XXX set an error? */ - - return sec_pkcs7_add_certificate (cinfo, cert); -} - - -static SECStatus -sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo, - PRArenaPool *poolp, - SECOidTag kind, PRBool detached, - SECOidTag encalg, int keysize) -{ - SECStatus rv; - - PORT_Assert (enccinfo != NULL && poolp != NULL); - if (enccinfo == NULL || poolp == NULL) - return SECFailure; - - /* - * XXX Some day we may want to allow for other kinds. That needs - * more work and modifications to the creation interface, etc. - * For now, allow but notice callers who pass in other kinds. - * They are responsible for creating the inner type and encoding, - * if it is other than DATA. - */ - PORT_Assert (kind == SEC_OID_PKCS7_DATA); - - enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind); - PORT_Assert (enccinfo->contentTypeTag - && enccinfo->contentTypeTag->offset == kind); - - rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType), - &(enccinfo->contentTypeTag->oid)); - if (rv != SECSuccess) - return rv; - - /* Save keysize and algorithm for later. */ - enccinfo->keysize = keysize; - enccinfo->encalg = encalg; - - return SECSuccess; -} - - -/* - * Add a recipient to a PKCS7 thing, verifying their cert first. - * Any error returns SECFailure. - */ -static SECStatus -sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate *cert, - SECCertUsage certusage, - CERTCertDBHandle *certdb) -{ - SECOidTag kind; - SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp; - SECItem *dummy; - void *mark; - int count; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - case SEC_OID_PKCS7_ENVELOPED_DATA: - { - SEC_PKCS7EnvelopedData *edp; - - edp = cinfo->content.envelopedData; - recipientinfosp = &(edp->recipientInfos); - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - recipientinfosp = &(saedp->recipientInfos); - } - break; - default: - return SECFailure; /* XXX set an error? */ - } - - /* - * XXX I think that CERT_VerifyCert should do this if *it* is passed - * a NULL database. - */ - if (certdb == NULL) { - certdb = CERT_GetDefaultCertDB(); - if (certdb == NULL) - return SECFailure; /* XXX set an error? */ - } - - if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), - cinfo->pwfn_arg, NULL) != SECSuccess) - { - /* XXX Did CERT_VerifyCert set an error? */ - return SECFailure; - } - - mark = PORT_ArenaMark (cinfo->poolp); - - recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp, - sizeof(SEC_PKCS7RecipientInfo)); - if (recipientinfo == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version, - SEC_PKCS7_RECIPIENT_INFO_VERSION); - if (dummy == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - PORT_Assert (dummy == &recipientinfo->version); - - recipientinfo->cert = CERT_DupCertificate (cert); - if (recipientinfo->cert == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); - if (recipientinfo->issuerAndSN == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - /* - * Okay, now recipientinfo is all set. We just need to put it into - * the main structure. - * - * If this is the first recipient, allocate a new recipientinfos array; - * otherwise, reallocate the array, making room for the new entry. - */ - recipientinfos = *recipientinfosp; - if (recipientinfos == NULL) { - count = 0; - recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc ( - cinfo->poolp, - 2 * sizeof(SEC_PKCS7RecipientInfo *)); - } else { - for (count = 0; recipientinfos[count] != NULL; count++) - ; - PORT_Assert (count); /* should be at least one already */ - recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow ( - cinfo->poolp, recipientinfos, - (count + 1) * sizeof(SEC_PKCS7RecipientInfo *), - (count + 2) * sizeof(SEC_PKCS7RecipientInfo *)); - } - - if (recipientinfos == NULL) { - PORT_ArenaRelease (cinfo->poolp, mark); - return SECFailure; - } - - recipientinfos[count] = recipientinfo; - recipientinfos[count + 1] = NULL; - - *recipientinfosp = recipientinfos; - - PORT_ArenaUnmark (cinfo->poolp, mark); - return SECSuccess; -} - - -/* - * Start a PKCS7 enveloping context. - * - * "cert" is the cert for the recipient. It will be checked for validity. - * - * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) - * XXX Maybe SECCertUsage should be split so that our caller just says - * "email" and *we* add the "recipient" part -- otherwise our caller - * could be lying about the usage; we do not want to allow encryption - * certs for signing or vice versa. - * - * "certdb" is the cert database to use for verifying the cert. - * It can be NULL if a default database is available (like in the client). - * - * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). - * - * "keysize" specifies the bulk encryption key size, in bits. - * - * The return value can be passed to functions which add things to - * it like more recipients, then eventually to SEC_PKCS7Encode() or to - * SEC_PKCS7EncoderStart() to create the encoded data, and finally to - * SEC_PKCS7DestroyContentInfo(). - * - * An error results in a return value of NULL and an error set. - * (Retrieve specific errors via PORT_GetError()/XP_GetError().) - */ -extern SEC_PKCS7ContentInfo * -SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, - SECCertUsage certusage, - CERTCertDBHandle *certdb, - SECOidTag encalg, - int keysize, - SECKEYGetPasswordKey pwfn, void *pwfn_arg) -{ - SEC_PKCS7ContentInfo *cinfo; - SEC_PKCS7EnvelopedData *envd; - SECStatus rv; - - cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA, - PR_FALSE, pwfn, pwfn_arg); - if (cinfo == NULL) - return NULL; - - rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - envd = cinfo->content.envelopedData; - PORT_Assert (envd != NULL); - - /* - * XXX Might we want to allow content types other than data? - * If so, via what interface? - */ - rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo), - cinfo->poolp, - SEC_OID_PKCS7_DATA, PR_FALSE, - encalg, keysize); - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - /* XXX Anything more to do here? */ - - return cinfo; -} - - -/* - * Add another recipient to an encrypted message. - * - * "cinfo" should be of type envelopedData or signedAndEnvelopedData; - * SECFailure will be returned if it is not. - * - * "cert" is the cert for the recipient. It will be checked for validity. - * - * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) - * XXX Maybe SECCertUsage should be split so that our caller just says - * "email" and *we* add the "recipient" part -- otherwise our caller - * could be lying about the usage; we do not want to allow encryption - * certs for signing or vice versa. - * - * "certdb" is the cert database to use for verifying the cert. - * It can be NULL if a default database is available (like in the client). - */ -SECStatus -SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, - CERTCertificate *cert, - SECCertUsage certusage, - CERTCertDBHandle *certdb) -{ - return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); -} - - -/* - * Create an empty PKCS7 data content info. - * - * An error results in a return value of NULL and an error set. - * (Retrieve specific errors via PORT_GetError()/XP_GetError().) - */ -SEC_PKCS7ContentInfo * -SEC_PKCS7CreateData (void) -{ - return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE, - NULL, NULL); -} - - -/* - * Create an empty PKCS7 encrypted content info. - * - * "algorithm" specifies the bulk encryption algorithm to use. - * - * An error results in a return value of NULL and an error set. - * (Retrieve specific errors via PORT_GetError()/XP_GetError().) - */ -SEC_PKCS7ContentInfo * -SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, - SECKEYGetPasswordKey pwfn, void *pwfn_arg) -{ - SEC_PKCS7ContentInfo *cinfo; - SECAlgorithmID *algid; - SEC_PKCS7EncryptedData *enc_data; - SECStatus rv; - - cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, - PR_FALSE, pwfn, pwfn_arg); - if (cinfo == NULL) - return NULL; - - enc_data = cinfo->content.encryptedData; - algid = &(enc_data->encContentInfo.contentEncAlg); - - switch (algorithm) { - case SEC_OID_RC2_CBC: - case SEC_OID_DES_EDE3_CBC: - case SEC_OID_DES_CBC: - rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); - break; - default: - { - /* - * Assume password-based-encryption. At least, try that. - */ - SECAlgorithmID *pbe_algid; - pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL); - if (pbe_algid == NULL) { - rv = SECFailure; - } else { - rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); - SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); - } - } - break; - } - - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo), - cinfo->poolp, - SEC_OID_PKCS7_DATA, PR_FALSE, - algorithm, keysize); - if (rv != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - return NULL; - } - - return cinfo; -} - |