Index: mozilla/security/nss/lib/pkcs7/p7create.c |
=================================================================== |
--- mozilla/security/nss/lib/pkcs7/p7create.c (revision 191424) |
+++ mozilla/security/nss/lib/pkcs7/p7create.c (working copy) |
@@ -1,1293 +0,0 @@ |
-/* This Source Code Form is subject to the terms of the Mozilla Public |
- * License, v. 2.0. If a copy of the MPL was not distributed with this |
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
- |
-/* |
- * PKCS7 creation. |
- * |
- * $Id: p7create.c,v 1.11 2012/04/25 14:50:06 gerv%gerv.net Exp $ |
- */ |
- |
-#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" |
-#include "secpkcs5.h" |
- |
-const int NSS_PBE_DEFAULT_ITERATION_COUNT = 2000; /* used in p12e.c too */ |
- |
-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 = DER_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); |
- |
- if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { |
- rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); |
- } else { |
- /* Assume password-based-encryption. |
- * Note: we can't generate pkcs5v2 from this interface. |
- * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting |
- * non-PBE oids and assuming that they are pkcs5v2 oids, but |
- * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular |
- * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData |
- * to create pkcs5v2 PBEs */ |
- SECAlgorithmID *pbe_algid; |
- pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, |
- NSS_PBE_DEFAULT_ITERATION_COUNT, |
- NULL); |
- if (pbe_algid == NULL) { |
- rv = SECFailure; |
- } else { |
- rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); |
- SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); |
- } |
- } |
- |
- 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; |
-} |
- |