summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/smime/cmsenvdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/smime/cmsenvdata.c')
-rw-r--r--security/nss/lib/smime/cmsenvdata.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/security/nss/lib/smime/cmsenvdata.c b/security/nss/lib/smime/cmsenvdata.c
new file mode 100644
index 000000000..f2c8e171d
--- /dev/null
+++ b/security/nss/lib/smime/cmsenvdata.c
@@ -0,0 +1,398 @@
+/* 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/. */
+
+/*
+ * CMS envelopedData methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "key.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+#include "secpkcs5.h"
+
+/*
+ * NSS_CMSEnvelopedData_Create - create an enveloped data message
+ */
+NSSCMSEnvelopedData *
+NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
+{
+ void *mark;
+ NSSCMSEnvelopedData *envd;
+ PLArenaPool *poolp;
+ SECStatus rv;
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
+ if (envd == NULL)
+ goto loser;
+
+ envd->cmsg = cmsg;
+
+ /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
+
+ rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo),
+ algorithm, NULL, keysize);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return envd;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
+ */
+void
+NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
+{
+ NSSCMSRecipientInfo **recipientinfos;
+ NSSCMSRecipientInfo *ri;
+
+ if (edp == NULL)
+ return;
+
+ recipientinfos = edp->recipientInfos;
+ if (recipientinfos == NULL)
+ return;
+
+ while ((ri = *recipientinfos++) != NULL)
+ NSS_CMSRecipientInfo_Destroy(ri);
+
+ NSS_CMSContentInfo_Destroy(&(edp->contentInfo));
+}
+
+/*
+ * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
+ */
+NSSCMSContentInfo *
+NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
+{
+ return &(envd->contentInfo);
+}
+
+/*
+ * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
+ *
+ * rip must be created on the same pool as edp - this is not enforced, though.
+ */
+SECStatus
+NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
+{
+ void *mark;
+ SECStatus rv;
+
+ /* XXX compare pools, if not same, copy rip into edp's pool */
+
+ PR_ASSERT(edp != NULL);
+ PR_ASSERT(rip != NULL);
+
+ mark = PORT_ArenaMark(edp->cmsg->poolp);
+
+ rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease(edp->cmsg->poolp, mark);
+ return SECFailure;
+ }
+
+ PORT_ArenaUnmark(edp->cmsg->poolp, mark);
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
+ *
+ * at this point, we need
+ * - recipientinfos set up with recipient's certificates
+ * - a content encryption algorithm (if none, 3DES will be used)
+ *
+ * this function will generate a random content encryption key (aka bulk key),
+ * initialize the recipientinfos with certificate identification and wrap the bulk key
+ * using the proper algorithm for every certificiate.
+ * it will finally set the bulk algorithm and key so that the encode step can find it.
+ */
+SECStatus
+NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
+{
+ int version;
+ NSSCMSRecipientInfo **recipientinfos;
+ NSSCMSContentInfo *cinfo;
+ PK11SymKey *bulkkey = NULL;
+ SECOidTag bulkalgtag;
+ CK_MECHANISM_TYPE type;
+ PK11SlotInfo *slot;
+ SECStatus rv;
+ SECItem *dummy;
+ PLArenaPool *poolp;
+ extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
+ void *mark = NULL;
+ int i;
+
+ poolp = envd->cmsg->poolp;
+ cinfo = &(envd->contentInfo);
+
+ recipientinfos = envd->recipientInfos;
+ if (recipientinfos == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+#if 0
+ PORT_SetErrorString("Cannot find recipientinfos to encode.");
+#endif
+ goto loser;
+ }
+
+ version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
+ if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
+ version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
+ } else {
+ for (i = 0; recipientinfos[i] != NULL; i++) {
+ if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
+ version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
+ break;
+ }
+ }
+ }
+ dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
+ if (dummy == NULL)
+ goto loser;
+
+ /* now we need to have a proper content encryption algorithm
+ * on the SMIME level, we would figure one out by looking at SMIME capabilities
+ * we cannot do that on our level, so if none is set already, we'll just go
+ * with one of the mandatory algorithms (3DES) */
+ if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
+ rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
+ if (rv != SECSuccess)
+ goto loser;
+ bulkalgtag = SEC_OID_DES_EDE3_CBC;
+ }
+
+ /* generate a random bulk key suitable for content encryption alg */
+ type = PK11_AlgtagToMechanism(bulkalgtag);
+ slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
+ if (slot == NULL)
+ goto loser; /* error has been set by PK11_GetBestSlot */
+
+ /* this is expensive... */
+ bulkkey = PK11_KeyGen(slot, type, NULL,
+ NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8,
+ envd->cmsg->pwfn_arg);
+ PK11_FreeSlot(slot);
+ if (bulkkey == NULL)
+ goto loser; /* error has been set by PK11_KeyGen */
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* Encrypt the bulk key with the public key of each recipient. */
+ for (i = 0; recipientinfos[i] != NULL; i++) {
+ rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
+ if (rv != SECSuccess)
+ goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
+ /* could be: alg not supported etc. */
+ }
+
+ /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
+ rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos,
+ NSSCMSRecipientInfoTemplate, NULL);
+ if (rv != SECSuccess)
+ goto loser; /* error has been set by NSS_CMSArray_SortByDER */
+
+ /* store the bulk key in the contentInfo so that the encoder can find it */
+ NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
+
+ PORT_ArenaUnmark(poolp, mark);
+
+ PK11_FreeSymKey(bulkkey);
+
+ return SECSuccess;
+
+loser:
+ if (mark != NULL)
+ PORT_ArenaRelease(poolp, mark);
+ if (bulkkey)
+ PK11_FreeSymKey(bulkkey);
+
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
+ *
+ * it is essential that this is called before the contentEncAlg is encoded, because
+ * setting up the encryption may generate IVs and thus change it!
+ */
+SECStatus
+NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
+{
+ NSSCMSContentInfo *cinfo;
+ PK11SymKey *bulkkey;
+ SECAlgorithmID *algid;
+ SECStatus rv;
+
+ cinfo = &(envd->contentInfo);
+
+ /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
+ bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
+ if (bulkkey == NULL)
+ return SECFailure;
+ algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
+ if (algid == NULL)
+ return SECFailure;
+
+ rv = NSS_CMSContentInfo_Private_Init(cinfo);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* this may modify algid (with IVs generated in a token).
+ * it is essential that algid is a pointer to the contentEncAlg data, not a
+ * pointer to a copy! */
+ cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
+ PK11_FreeSymKey(bulkkey);
+ if (cinfo->privateInfo->ciphcx == NULL)
+ return SECFailure;
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
+ */
+SECStatus
+NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
+{
+ if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
+ NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
+ envd->contentInfo.privateInfo->ciphcx = NULL;
+ }
+
+ /* nothing else to do after data */
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo,
+ * derive bulk key & set up our contentinfo
+ */
+SECStatus
+NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
+{
+ NSSCMSRecipientInfo *ri;
+ PK11SymKey *bulkkey = NULL;
+ SECOidTag bulkalgtag;
+ SECAlgorithmID *bulkalg;
+ SECStatus rv = SECFailure;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSRecipient **recipient_list = NULL;
+ NSSCMSRecipient *recipient;
+ int rlIndex;
+
+ if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+#if 0
+ PORT_SetErrorString("No recipient data in envelope.");
+#endif
+ goto loser;
+ }
+
+ /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */
+ /* get the cert and private key for it right away */
+ recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
+ if (recipient_list == NULL)
+ goto loser;
+
+ /* what about multiple recipientInfos that match?
+ * especially if, for some reason, we could not produce a bulk key with the first match?!
+ * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
+ * maybe later... */
+ rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
+
+ /* if that fails, then we're not an intended recipient and cannot decrypt */
+ if (rlIndex < 0) {
+ PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
+#if 0
+ PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
+#endif
+ goto loser;
+ }
+
+ recipient = recipient_list[rlIndex];
+ if (!recipient->cert || !recipient->privkey) {
+ /* XXX should set an error code ?!? */
+ goto loser;
+ }
+ /* get a pointer to "our" recipientinfo */
+ ri = envd->recipientInfos[recipient->riIndex];
+
+ cinfo = &(envd->contentInfo);
+ bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
+ if (bulkalgtag == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ } else
+ bulkkey =
+ NSS_CMSRecipientInfo_UnwrapBulkKey(ri, recipient->subIndex,
+ recipient->cert,
+ recipient->privkey,
+ bulkalgtag);
+ if (bulkkey == NULL) {
+ /* no success finding a bulk key */
+ goto loser;
+ }
+
+ NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
+
+ bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
+
+ rv = NSS_CMSContentInfo_Private_Init(cinfo);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = SECFailure;
+ cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
+ if (cinfo->privateInfo->ciphcx == NULL)
+ goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */
+
+ rv = SECSuccess;
+
+loser:
+ if (bulkkey)
+ PK11_FreeSymKey(bulkkey);
+ if (recipient_list != NULL)
+ nss_cms_recipient_list_destroy(recipient_list);
+ return rv;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
+ */
+SECStatus
+NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
+{
+ if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
+ NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
+ envd->contentInfo.privateInfo->ciphcx = NULL;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
+ */
+SECStatus
+NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
+{
+ /* apply final touches */
+ return SECSuccess;
+}