summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/smime
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/smime/Makefile48
-rw-r--r--security/nss/lib/smime/cms.h1153
-rw-r--r--security/nss/lib/smime/cmsarray.c186
-rw-r--r--security/nss/lib/smime/cmsasn1.c491
-rw-r--r--security/nss/lib/smime/cmsattr.c425
-rw-r--r--security/nss/lib/smime/cmscinfo.c443
-rw-r--r--security/nss/lib/smime/cmscipher.c722
-rw-r--r--security/nss/lib/smime/cmsdecode.c736
-rw-r--r--security/nss/lib/smime/cmsdigdata.c212
-rw-r--r--security/nss/lib/smime/cmsdigest.c259
-rw-r--r--security/nss/lib/smime/cmsencdata.c262
-rw-r--r--security/nss/lib/smime/cmsencode.c762
-rw-r--r--security/nss/lib/smime/cmsenvdata.c403
-rw-r--r--security/nss/lib/smime/cmslocal.h351
-rw-r--r--security/nss/lib/smime/cmsmessage.c345
-rw-r--r--security/nss/lib/smime/cmspubkey.c285
-rw-r--r--security/nss/lib/smime/cmsrecinfo.c669
-rw-r--r--security/nss/lib/smime/cmsreclist.c170
-rw-r--r--security/nss/lib/smime/cmsreclist.h27
-rw-r--r--security/nss/lib/smime/cmssigdata.c1141
-rw-r--r--security/nss/lib/smime/cmssiginfo.c1081
-rw-r--r--security/nss/lib/smime/cmst.h491
-rw-r--r--security/nss/lib/smime/cmsudf.c440
-rw-r--r--security/nss/lib/smime/cmsutil.c371
-rw-r--r--security/nss/lib/smime/config.mk56
-rw-r--r--security/nss/lib/smime/exports.gyp34
-rw-r--r--security/nss/lib/smime/manifest.mn51
-rw-r--r--security/nss/lib/smime/smime.def293
-rw-r--r--security/nss/lib/smime/smime.gyp80
-rw-r--r--security/nss/lib/smime/smime.h141
-rw-r--r--security/nss/lib/smime/smime.rc68
-rw-r--r--security/nss/lib/smime/smimemessage.c183
-rw-r--r--security/nss/lib/smime/smimesym.c12
-rw-r--r--security/nss/lib/smime/smimeutil.c795
-rw-r--r--security/nss/lib/smime/smimever.c18
35 files changed, 13204 insertions, 0 deletions
diff --git a/security/nss/lib/smime/Makefile b/security/nss/lib/smime/Makefile
new file mode 100644
index 0000000000..a9cebcfecf
--- /dev/null
+++ b/security/nss/lib/smime/Makefile
@@ -0,0 +1,48 @@
+#! gmake
+#
+# 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/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+
diff --git a/security/nss/lib/smime/cms.h b/security/nss/lib/smime/cms.h
new file mode 100644
index 0000000000..f4a8a39e9e
--- /dev/null
+++ b/security/nss/lib/smime/cms.h
@@ -0,0 +1,1153 @@
+/* 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/. */
+
+/*
+ * Interfaces of the CMS implementation.
+ */
+
+#ifndef _CMS_H_
+#define _CMS_H_
+
+#include "seccomon.h"
+
+#include "secoidt.h"
+#include "certt.h"
+#include "keythi.h"
+#include "hasht.h"
+#include "cmst.h"
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/************************************************************************
+ * cmsdecode.c - CMS decoding
+ ************************************************************************/
+
+/*
+ * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
+ *
+ * "poolp" - pointer to arena for message, or NULL if new pool should be created
+ * "cb", "cb_arg" - callback function and argument for delivery of inner content
+ * inner content will be stored in the message if cb is NULL.
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ */
+extern NSSCMSDecoderContext *
+NSS_CMSDecoder_Start(PLArenaPool *poolp,
+ NSSCMSContentCallback cb, void *cb_arg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg);
+
+/*
+ * NSS_CMSDecoder_Update - feed DER-encoded data to decoder
+ */
+extern SECStatus
+NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, unsigned long len);
+
+/*
+ * NSS_CMSDecoder_Cancel - cancel a decoding process
+ */
+extern void
+NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx);
+
+/*
+ * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
+ */
+extern NSSCMSMessage *
+NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx);
+
+/*
+ * NSS_CMSMessage_CreateFromDER - decode a CMS message from DER encoded data
+ */
+extern NSSCMSMessage *
+NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
+ NSSCMSContentCallback cb, void *cb_arg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg);
+
+/************************************************************************
+ * cmsencode.c - CMS encoding
+ ************************************************************************/
+
+/*
+ * NSS_CMSEncoder_Start - set up encoding of a CMS message
+ *
+ * "cmsg" - message to encode
+ * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
+ * will not be called if NULL.
+ * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
+ * "destpoolp" - pool to allocate DER-encoded output in
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ * "detached_digestalgs", "detached_digests" - digests from detached content
+ */
+extern NSSCMSEncoderContext *
+NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
+ NSSCMSContentCallback outputfn, void *outputarg,
+ SECItem *dest, PLArenaPool *destpoolp,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
+ SECAlgorithmID **detached_digestalgs, SECItem **detached_digests);
+
+/*
+ * NSS_CMSEncoder_Update - take content data delivery from the user
+ *
+ * "p7ecx" - encoder context
+ * "data" - content data
+ * "len" - length of content data
+ */
+extern SECStatus
+NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
+
+/*
+ * NSS_CMSEncoder_Cancel - stop all encoding
+ */
+extern SECStatus
+NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx);
+
+/*
+ * NSS_CMSEncoder_Finish - signal the end of data
+ *
+ * we need to walk down the chain of encoders and the finish them from the innermost out
+ */
+extern SECStatus
+NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx);
+
+/************************************************************************
+ * cmsmessage.c - CMS message object
+ ************************************************************************/
+
+/*
+ * NSS_CMSMessage_Create - create a CMS message object
+ *
+ * "poolp" - arena to allocate memory from, or NULL if new arena should be created
+ */
+extern NSSCMSMessage *
+NSS_CMSMessage_Create(PLArenaPool *poolp);
+
+/*
+ * NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding
+ *
+ * "cmsg" - message object
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ * "detached_digestalgs", "detached_digests" - digests from detached content
+ *
+ * used internally.
+ */
+extern void
+NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
+ SECAlgorithmID **detached_digestalgs, SECItem **detached_digests);
+
+/*
+ * NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces.
+ */
+extern void
+NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_Copy - return a copy of the given message.
+ *
+ * The copy may be virtual or may be real -- either way, the result needs
+ * to be passed to NSS_CMSMessage_Destroy later (as does the original).
+ */
+extern NSSCMSMessage *
+NSS_CMSMessage_Copy(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_GetArena - return a pointer to the message's arena pool
+ */
+extern PLArenaPool *
+NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo
+ */
+extern NSSCMSContentInfo *
+NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg);
+
+/*
+ * Return a pointer to the actual content.
+ * In the case of those types which are encrypted, this returns the *plain* content.
+ * In case of nested contentInfos, this descends and retrieves the innermost content.
+ */
+extern SECItem *
+NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message
+ *
+ * CMS data content objects do not count.
+ */
+extern int
+NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_ContentLevel - find content level #n
+ *
+ * CMS data content objects do not count.
+ */
+extern NSSCMSContentInfo *
+NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n);
+
+/*
+ * NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way
+ */
+extern PRBool
+NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage
+ */
+extern PRBool
+NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_IsSigned - see if message contains a signed submessage
+ *
+ * If the CMS message has a SignedData with a signature (not just a SignedData)
+ * return true; false otherwise. This can/should be called before calling
+ * VerifySignature, which will always indicate failure if no signature is
+ * present, but that does not mean there even was a signature!
+ * Note that the content itself can be empty (detached content was sent
+ * another way); it is the presence of the signature that matters.
+ */
+extern PRBool
+NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg);
+
+/*
+ * NSS_CMSMessage_IsContentEmpty - see if content is empty
+ *
+ * returns PR_TRUE is innermost content length is < minLen
+ * XXX need the encrypted content length (why?)
+ */
+extern PRBool
+NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen);
+
+/************************************************************************
+ * cmscinfo.c - CMS contentInfo methods
+ ************************************************************************/
+
+/*
+ * NSS_CMSContentInfo_Destroy - destroy a CMS contentInfo and all of its sub-pieces.
+ */
+extern void
+NSS_CMSContentInfo_Destroy(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_GetChildContentInfo - get content's contentInfo (if it exists)
+ */
+extern NSSCMSContentInfo *
+NSS_CMSContentInfo_GetChildContentInfo(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_SetContent - set cinfo's content type & content to CMS object
+ */
+extern SECStatus
+NSS_CMSContentInfo_SetContent(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, SECOidTag type, void *ptr);
+
+/*
+ * NSS_CMSContentInfo_SetContent_XXXX - typesafe wrappers for NSS_CMSContentInfo_SetType
+ * set cinfo's content type & content to CMS object
+ */
+extern SECStatus
+NSS_CMSContentInfo_SetContent_Data(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, SECItem *data, PRBool detached);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContent_SignedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSSignedData *sigd);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContent_EnvelopedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSEnvelopedData *envd);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContent_DigestedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSDigestedData *digd);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContent_EncryptedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSEncryptedData *encd);
+
+/*
+ * turn off streaming for this content type.
+ * This could fail with SEC_ERROR_NO_MEMORY in memory constrained conditions.
+ */
+extern SECStatus
+NSS_CMSContentInfo_SetDontStream(NSSCMSContentInfo *cinfo, PRBool dontStream);
+
+/*
+ * NSS_CMSContentInfo_GetContent - get pointer to inner content
+ *
+ * needs to be casted...
+ */
+extern void *
+NSS_CMSContentInfo_GetContent(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_GetInnerContent - get pointer to innermost content
+ *
+ * this is typically only called by NSS_CMSMessage_GetContent()
+ */
+extern SECItem *
+NSS_CMSContentInfo_GetInnerContent(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_GetContentType{Tag,OID} - find out (saving pointer to lookup result
+ * for future reference) and return the inner content type.
+ */
+extern SECOidTag
+NSS_CMSContentInfo_GetContentTypeTag(NSSCMSContentInfo *cinfo);
+
+extern SECItem *
+NSS_CMSContentInfo_GetContentTypeOID(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_GetContentEncAlgTag - find out (saving pointer to lookup result
+ * for future reference) and return the content encryption algorithm tag.
+ */
+extern SECOidTag
+NSS_CMSContentInfo_GetContentEncAlgTag(NSSCMSContentInfo *cinfo);
+
+/*
+ * NSS_CMSContentInfo_GetContentEncAlg - find out and return the content encryption algorithm tag.
+ */
+extern SECAlgorithmID *
+NSS_CMSContentInfo_GetContentEncAlg(NSSCMSContentInfo *cinfo);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContentEncAlg(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
+ SECOidTag bulkalgtag, SECItem *parameters, int keysize);
+
+extern SECStatus
+NSS_CMSContentInfo_SetContentEncAlgID(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
+ SECAlgorithmID *algid, int keysize);
+
+extern void
+NSS_CMSContentInfo_SetBulkKey(NSSCMSContentInfo *cinfo, PK11SymKey *bulkkey);
+
+extern PK11SymKey *
+NSS_CMSContentInfo_GetBulkKey(NSSCMSContentInfo *cinfo);
+
+extern int
+NSS_CMSContentInfo_GetBulkKeySize(NSSCMSContentInfo *cinfo);
+
+/************************************************************************
+ * cmsutil.c - CMS misc utility functions
+ ************************************************************************/
+
+/*
+ * NSS_CMSArray_SortByDER - sort array of objects by objects' DER encoding
+ *
+ * make sure that the order of the objects guarantees valid DER (which must be
+ * in lexigraphically ascending order for a SET OF); if reordering is necessary it
+ * will be done in place (in objs).
+ */
+extern SECStatus
+NSS_CMSArray_SortByDER(void **objs, const SEC_ASN1Template *objtemplate, void **objs2);
+
+/*
+ * NSS_CMSUtil_DERCompare - for use with NSS_CMSArray_Sort to
+ * sort arrays of SECItems containing DER
+ */
+extern int
+NSS_CMSUtil_DERCompare(void *a, void *b);
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgID - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algid - algorithmid of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+extern int
+NSS_CMSAlgArray_GetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid);
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgID - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algiddata - id of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+extern int
+NSS_CMSAlgArray_GetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
+
+extern const SECHashObject *
+NSS_CMSUtil_GetHashObjByAlgID(SECAlgorithmID *algid);
+
+extern const SEC_ASN1Template *
+NSS_CMSUtil_GetTemplateByTypeTag(SECOidTag type);
+
+extern size_t
+NSS_CMSUtil_GetSizeByTypeTag(SECOidTag type);
+
+extern NSSCMSContentInfo *
+NSS_CMSContent_GetContentInfo(void *msg, SECOidTag type);
+
+extern const char *
+NSS_CMSUtil_VerificationStatusToString(NSSCMSVerificationStatus vs);
+
+/************************************************************************
+ * cmssigdata.c - CMS signedData methods
+ ************************************************************************/
+
+extern NSSCMSSignedData *
+NSS_CMSSignedData_Create(NSSCMSMessage *cmsg);
+
+extern void
+NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
+ * before start of encoding.
+ *
+ * In detail:
+ * - find out about the right value to put into sigd->version
+ * - come up with a list of digestAlgorithms (which should be the union of the algorithms
+ * in the signerinfos).
+ * If we happen to have a pre-set list of algorithms (and digest values!), we
+ * check if we have all the signerinfos' algorithms. If not, this is an error.
+ */
+extern SECStatus
+NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd);
+
+extern SECStatus
+NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - create the signatures in all the SignerInfos
+ *
+ * Please note that nothing is done to the Certificates and CRLs in the message - this
+ * is entirely the responsibility of our callers.
+ */
+extern SECStatus
+NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd);
+
+extern SECStatus
+NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a SignedData
+ * after all the encapsulated data was passed through the decoder.
+ */
+extern SECStatus
+NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
+ * after all decoding is finished.
+ */
+extern SECStatus
+NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
+ */
+extern NSSCMSSignerInfo **
+NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd);
+
+extern int
+NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd);
+
+extern NSSCMSSignerInfo *
+NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i);
+
+/*
+ * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
+ */
+extern SECAlgorithmID **
+NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
+ */
+extern NSSCMSContentInfo *
+NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
+ */
+extern SECItem **
+NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd);
+
+extern SECStatus
+NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
+ SECCertUsage certusage, PRBool keepcerts);
+
+/*
+ * NSS_CMSSignedData_HasDigests - see if we have digests in place
+ */
+extern PRBool
+NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd);
+
+/*
+ * NSS_CMSSignedData_VerifySignerInfo - check a signature.
+ *
+ * The digests were either calculated during decoding (and are stored in the
+ * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
+ *
+ * The verification checks if the signing cert is valid and has a trusted chain
+ * for the purpose specified by "certusage".
+ */
+extern SECStatus
+NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i, CERTCertDBHandle *certdb,
+ SECCertUsage certusage);
+
+/*
+ * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
+*/
+extern SECStatus
+NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd,
+ CERTCertDBHandle *certdb,
+ SECCertUsage usage);
+
+extern SECStatus
+NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist);
+
+/*
+ * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
+ */
+extern SECStatus
+NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert);
+
+extern SECStatus
+NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert);
+
+extern PRBool
+NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd);
+
+extern SECStatus
+NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
+ NSSCMSSignerInfo *signerinfo);
+
+extern SECStatus
+NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
+ SECAlgorithmID **digestalgs,
+ SECItem **digests);
+
+extern SECStatus
+NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
+ SECOidTag digestalgtag,
+ SECItem *digestdata);
+
+extern SECStatus
+NSS_CMSSignedData_AddDigest(PLArenaPool *poolp,
+ NSSCMSSignedData *sigd,
+ SECOidTag digestalgtag,
+ SECItem *digest);
+
+extern SECItem *
+NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag);
+
+/*
+ * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
+ *
+ * cert - base certificates that will be included
+ * include_chain - if true, include the complete cert chain for cert
+ *
+ * More certs and chains can be added via AddCertificate and AddCertChain.
+ *
+ * An error results in a return value of NULL and an error set.
+ */
+extern NSSCMSSignedData *
+NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain);
+
+/************************************************************************
+ * cmssiginfo.c - signerinfo methods
+ ************************************************************************/
+
+extern NSSCMSSignerInfo *
+NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag);
+extern NSSCMSSignerInfo *
+NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID, SECKEYPublicKey *pubKey, SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
+
+/*
+ * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
+ */
+extern void
+NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si);
+
+/*
+ * NSS_CMSSignerInfo_Sign - sign something
+ *
+ */
+extern SECStatus
+NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType);
+
+extern SECStatus
+NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
+ SECCertUsage certusage);
+
+/*
+ * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
+ *
+ * Just verifies the signature. The assumption is that verification of the certificate
+ * is done already.
+ */
+extern SECStatus
+NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType);
+
+extern NSSCMSVerificationStatus
+NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo);
+
+extern SECOidData *
+NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo);
+
+extern SECOidTag
+NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo);
+
+extern int
+NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo);
+
+extern CERTCertificateList *
+NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo);
+
+/*
+ * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
+ * in UTCTime format, of a CMS signerInfo.
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to XXXX (what?)
+ * A return value of NULL is an error.
+ */
+extern SECStatus
+NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime);
+
+/*
+ * Return the signing cert of a CMS signerInfo.
+ *
+ * the certs in the enclosing SignedData must have been imported already
+ */
+extern CERTCertificate *
+NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb);
+
+/*
+ * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to allocated memory, which must be freed with PORT_Free.
+ * A return value of NULL is an error.
+ */
+extern char *
+NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo);
+
+/*
+ * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to allocated memory, which must be freed.
+ * A return value of NULL is an error.
+ */
+extern char *
+NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo);
+
+/*
+ * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr);
+
+/*
+ * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
+ * unauthenticated attributes of "signerinfo".
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr);
+
+/*
+ * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * 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 do nothing.
+ *
+ * XXX This will probably just shove the current time into "signerinfo"
+ * but it will not actually get signed until the entire item is
+ * processed for encoding. Is this (expected to be small) delay okay?
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t);
+
+/*
+ * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed
+ * messages for email (S/MIME).
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo);
+
+/*
+ * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed messages for email (S/MIME).
+ */
+SECStatus
+NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb);
+
+/*
+ * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
+ *
+ * This is expected to be included in outgoing signed messages for email (S/MIME),
+ * if compatibility with Microsoft mail clients is wanted.
+ */
+SECStatus
+NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb);
+
+/*
+ * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
+ SECOidTag digestalg, CERTCertificate signingcert);
+
+/*
+ * XXXX the following needs to be done in the S/MIME layer code
+ * after signature of a signerinfo is verified
+ */
+extern SECStatus
+NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo);
+
+/*
+ * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
+ */
+extern SECStatus
+NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage);
+
+/************************************************************************
+ * cmsenvdata.c - CMS envelopedData methods
+ ************************************************************************/
+
+/*
+ * NSS_CMSEnvelopedData_Create - create an enveloped data message
+ */
+extern NSSCMSEnvelopedData *
+NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize);
+
+/*
+ * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
+ */
+extern void
+NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp);
+
+/*
+ * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
+ */
+extern NSSCMSContentInfo *
+NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd);
+
+/*
+ * 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.
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip);
+
+/*
+ * 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.
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd);
+
+/*
+ * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd);
+
+/*
+ * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd);
+
+/*
+ * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo,
+ * derive bulk key & set up our contentinfo
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd);
+
+/*
+ * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd);
+
+/*
+ * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
+ */
+extern SECStatus
+NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd);
+
+/************************************************************************
+ * cmsrecinfo.c - CMS recipientInfo methods
+ ************************************************************************/
+
+/*
+ * NSS_CMSRecipientInfo_Create - create a recipientinfo
+ *
+ * we currently do not create KeyAgreement recipientinfos with multiple recipientEncryptedKeys
+ * the certificate is supposed to have been verified by the caller
+ */
+extern NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert);
+
+extern NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg,
+ SECItem *subjKeyID,
+ SECKEYPublicKey *pubKey);
+
+extern NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert(NSSCMSMessage *cmsg,
+ CERTCertificate *cert);
+
+/*
+ * NSS_CMSRecipientInfo_CreateNew - create a blank recipientinfo for
+ * applications which want to encode their own CMS structures and
+ * key exchange types.
+ */
+extern NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateNew(void *pwfn_arg);
+
+/*
+ * NSS_CMSRecipientInfo_CreateFromDER - create a recipientinfo from partially
+ * decoded DER data for applications which want to encode their own CMS
+ * structures and key exchange types.
+ */
+extern NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateFromDER(SECItem *input, void *pwfn_arg);
+
+extern void
+NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri);
+
+/*
+ * NSS_CMSRecipientInfo_GetCertAndKey - retrieve the cert and key from the
+ * recipientInfo struct. If retcert or retkey are NULL, the cert or
+ * key (respectively) would not be returned). This function is a no-op if both
+ * retcert and retkey are NULL. Caller inherits ownership of the cert and key
+ * he requested (and is responsible to free them).
+ */
+SECStatus NSS_CMSRecipientInfo_GetCertAndKey(NSSCMSRecipientInfo *ri,
+ CERTCertificate **retcert,
+ SECKEYPrivateKey **retkey);
+
+extern int
+NSS_CMSRecipientInfo_GetVersion(NSSCMSRecipientInfo *ri);
+
+extern SECItem *
+NSS_CMSRecipientInfo_GetEncryptedKey(NSSCMSRecipientInfo *ri, int subIndex);
+
+/*
+ * NSS_CMSRecipientInfo_Encode - encode an NSS_CMSRecipientInfo as ASN.1
+ */
+SECStatus NSS_CMSRecipientInfo_Encode(PLArenaPool *poolp,
+ const NSSCMSRecipientInfo *src,
+ SECItem *returned);
+
+extern SECOidTag
+NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri);
+
+extern SECStatus
+NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey,
+ SECOidTag bulkalgtag);
+
+extern PK11SymKey *
+NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex,
+ CERTCertificate *cert, SECKEYPrivateKey *privkey,
+ SECOidTag bulkalgtag);
+
+/************************************************************************
+ * cmsencdata.c - CMS encryptedData methods
+ ************************************************************************/
+/*
+ * NSS_CMSEncryptedData_Create - create an empty encryptedData object.
+ *
+ * "algorithm" specifies the bulk encryption algorithm to use.
+ * "keysize" is the key size.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern NSSCMSEncryptedData *
+NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize);
+
+/*
+ * NSS_CMSEncryptedData_Destroy - destroy an encryptedData object
+ */
+extern void
+NSS_CMSEncryptedData_Destroy(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_GetContentInfo - return pointer to encryptedData object's contentInfo
+ */
+extern NSSCMSContentInfo *
+NSS_CMSEncryptedData_GetContentInfo(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData
+ * before encoding begins.
+ *
+ * In particular:
+ * - set the correct version value.
+ * - get the encryption key
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Encode_BeforeData - set up encryption
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Encode_BeforeData(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Encode_AfterData - finalize this encryptedData for encoding
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Encode_AfterData(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Decode_BeforeData - find bulk key & set up decryption
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Decode_BeforeData(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Decode_AfterData - finish decrypting this encryptedData's content
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Decode_AfterData(NSSCMSEncryptedData *encd);
+
+/*
+ * NSS_CMSEncryptedData_Decode_AfterEnd - finish decoding this encryptedData
+ */
+extern SECStatus
+NSS_CMSEncryptedData_Decode_AfterEnd(NSSCMSEncryptedData *encd);
+
+/************************************************************************
+ * cmsdigdata.c - CMS encryptedData methods
+ ************************************************************************/
+/*
+ * NSS_CMSDigestedData_Create - create a digestedData object (presumably for encoding)
+ *
+ * version will be set by NSS_CMSDigestedData_Encode_BeforeStart
+ * digestAlg is passed as parameter
+ * contentInfo must be filled by the user
+ * digest will be calculated while encoding
+ */
+extern NSSCMSDigestedData *
+NSS_CMSDigestedData_Create(NSSCMSMessage *cmsg, SECAlgorithmID *digestalg);
+
+/*
+ * NSS_CMSDigestedData_Destroy - destroy a digestedData object
+ */
+extern void
+NSS_CMSDigestedData_Destroy(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_GetContentInfo - return pointer to digestedData object's contentInfo
+ */
+extern NSSCMSContentInfo *
+NSS_CMSDigestedData_GetContentInfo(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Encode_BeforeStart - do all the necessary things to a DigestedData
+ * before encoding begins.
+ *
+ * In particular:
+ * - set the right version number. The contentInfo's content type must be set up already.
+ */
+extern SECStatus
+NSS_CMSDigestedData_Encode_BeforeStart(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Encode_BeforeData - do all the necessary things to a DigestedData
+ * before the encapsulated data is passed through the encoder.
+ *
+ * In detail:
+ * - set up the digests if necessary
+ */
+extern SECStatus
+NSS_CMSDigestedData_Encode_BeforeData(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Encode_AfterData - do all the necessary things to a DigestedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - finish the digests
+ */
+extern SECStatus
+NSS_CMSDigestedData_Encode_AfterData(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Decode_BeforeData - do all the necessary things to a DigestedData
+ * before the encapsulated data is passed through the encoder.
+ *
+ * In detail:
+ * - set up the digests if necessary
+ */
+extern SECStatus
+NSS_CMSDigestedData_Decode_BeforeData(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Decode_AfterData - do all the necessary things to a DigestedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - finish the digests
+ */
+extern SECStatus
+NSS_CMSDigestedData_Decode_AfterData(NSSCMSDigestedData *digd);
+
+/*
+ * NSS_CMSDigestedData_Decode_AfterEnd - finalize a digestedData.
+ *
+ * In detail:
+ * - check the digests for equality
+ */
+extern SECStatus
+NSS_CMSDigestedData_Decode_AfterEnd(NSSCMSDigestedData *digd);
+
+/************************************************************************
+ * cmsdigest.c - digestion routines
+ ************************************************************************/
+
+/*
+ * NSS_CMSDigestContext_StartMultiple - start digest calculation using all the
+ * digest algorithms in "digestalgs" in parallel.
+ */
+extern NSSCMSDigestContext *
+NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs);
+
+/*
+ * NSS_CMSDigestContext_StartSingle - same as NSS_CMSDigestContext_StartMultiple, but
+ * only one algorithm.
+ */
+extern NSSCMSDigestContext *
+NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg);
+
+/*
+ * NSS_CMSDigestContext_Update - feed more data into the digest machine
+ */
+extern void
+NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx, const unsigned char *data, int len);
+
+/*
+ * NSS_CMSDigestContext_Cancel - cancel digesting operation
+ */
+extern void
+NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx);
+
+/*
+ * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
+ * into an array of SECItems (allocated on poolp)
+ */
+extern SECStatus
+NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx, PLArenaPool *poolp,
+ SECItem ***digestsp);
+
+/*
+ * NSS_CMSDigestContext_FinishSingle - same as NSS_CMSDigestContext_FinishMultiple,
+ * but for one digest.
+ */
+extern SECStatus
+NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx, PLArenaPool *poolp,
+ SECItem *digest);
+
+/************************************************************************
+ *
+ ************************************************************************/
+
+/* shortcuts for basic use */
+
+/*
+ * NSS_CMSDEREncode - DER Encode a CMS message, with input being
+ * the plaintext message and derOut being the output,
+ * stored in arena's pool.
+ */
+extern SECStatus
+NSS_CMSDEREncode(NSSCMSMessage *cmsg, SECItem *input, SECItem *derOut,
+ PLArenaPool *arena);
+
+/************************************************************************
+ *
+ ************************************************************************/
+
+/*
+ * define new S/MIME content type entries
+ *
+ * S/MIME uses the builtin PKCS7 oid types for encoding and decoding the
+ * various S/MIME content. Some applications have their own content type
+ * which is different from the standard content type defined by S/MIME.
+ *
+ * This function allows you to register new content types. There are basically
+ * Two different types of content, Wrappping content, and Data.
+ *
+ * For data types, All the functions below can be zero or NULL excext
+ * type and is isData, which should be your oid tag and PR_FALSE respectively
+ *
+ * For wrapping types, everything must be provided, or you will get encoder
+ * failures.
+ *
+ * If NSS doesn't already define the OID that you need, you can register
+ * your own with SECOID_AddEntry.
+ *
+ * Once you have defined your new content type, you can pass your new content
+ * type to NSS_CMSContentInfo_SetContent().
+ *
+ * If you are using a wrapping type you can pass your own data structure in
+ * the ptr field, but it must contain and embedded NSSCMSGenericWrappingData
+ * structure as the first element. The size you pass to
+ * NSS_CMSType_RegisterContentType is the total size of your self defined
+ * data structure. NSS_CMSContentInfo_GetContent will return that data
+ * structure from the content info. Your ASN1Template will be evaluated
+ * against that data structure.
+ */
+SECStatus NSS_CMSType_RegisterContentType(SECOidTag type,
+ SEC_ASN1Template *asn1Template, size_t size,
+ NSSCMSGenericWrapperDataDestroy destroy,
+ NSSCMSGenericWrapperDataCallback decode_before,
+ NSSCMSGenericWrapperDataCallback decode_after,
+ NSSCMSGenericWrapperDataCallback decode_end,
+ NSSCMSGenericWrapperDataCallback encode_start,
+ NSSCMSGenericWrapperDataCallback encode_before,
+ NSSCMSGenericWrapperDataCallback encode_after,
+ PRBool isData);
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _CMS_H_ */
diff --git a/security/nss/lib/smime/cmsarray.c b/security/nss/lib/smime/cmsarray.c
new file mode 100644
index 0000000000..6665d12bee
--- /dev/null
+++ b/security/nss/lib/smime/cmsarray.c
@@ -0,0 +1,186 @@
+/* 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 array functions.
+ */
+
+#include "cmslocal.h"
+
+#include "secerr.h"
+
+/*
+ * ARRAY FUNCTIONS
+ *
+ * In NSS, arrays are rather primitive arrays of pointers.
+ * Makes it easy to walk the array, but hard to count elements
+ * and manage the storage.
+ *
+ * This is a feeble attempt to encapsulate the functionality
+ * and get rid of hundreds of lines of similar code
+ */
+
+/*
+ * NSS_CMSArray_Alloc - allocate an array in an arena
+ *
+ * This allocates space for the array of pointers
+ */
+void **
+NSS_CMSArray_Alloc(PLArenaPool *poolp, int n)
+{
+ return (void **)PORT_ArenaZAlloc(poolp, n * sizeof(void *));
+}
+
+/*
+ * NSS_CMSArray_Add - add an element to the end of an array
+ *
+ * The array of pointers is either created (if array was empty before) or grown.
+ */
+SECStatus
+NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj)
+{
+ void **p;
+ int n;
+ void **dest;
+
+ PORT_Assert(array != NULL);
+ if (array == NULL)
+ return SECFailure;
+
+ if (*array == NULL) {
+ dest = (void **)PORT_ArenaAlloc(poolp, 2 * sizeof(void *));
+ n = 0;
+ } else {
+ n = 0;
+ p = *array;
+ while (*p++)
+ n++;
+ dest = (void **)PORT_ArenaGrow(poolp,
+ *array,
+ (n + 1) * sizeof(void *),
+ (n + 2) * sizeof(void *));
+ }
+
+ if (dest == NULL)
+ return SECFailure;
+
+ dest[n] = obj;
+ dest[n + 1] = NULL;
+ *array = dest;
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSArray_IsEmpty - check if array is empty
+ */
+PRBool
+NSS_CMSArray_IsEmpty(void **array)
+{
+ return (array == NULL || array[0] == NULL);
+}
+
+/*
+ * NSS_CMSArray_Count - count number of elements in array
+ */
+int
+NSS_CMSArray_Count(void **array)
+{
+ int n = 0;
+
+ if (array == NULL)
+ return 0;
+
+ while (*array++ != NULL)
+ n++;
+
+ return n;
+}
+
+/*
+ * NSS_CMSArray_Sort - sort an array in place
+ *
+ * If "secondary" or "tertiary are not NULL, it must be arrays with the same
+ * number of elements as "primary". The same reordering will get applied to it.
+ *
+ * "compare" is a function that returns
+ * < 0 when the first element is less than the second
+ * = 0 when the first element is equal to the second
+ * > 0 when the first element is greater than the second
+ * to acheive ascending ordering.
+ */
+void
+NSS_CMSArray_Sort(void **primary, int (*compare)(void *, void *), void **secondary, void **tertiary)
+{
+ int n, i, limit, lastxchg;
+ void *tmp;
+
+ n = NSS_CMSArray_Count(primary);
+
+ PORT_Assert(secondary == NULL || NSS_CMSArray_Count(secondary) == n);
+ PORT_Assert(tertiary == NULL || NSS_CMSArray_Count(tertiary) == n);
+
+ if (n <= 1) /* ordering is fine */
+ return;
+
+ /* yes, ladies and gentlemen, it's BUBBLE SORT TIME! */
+ limit = n - 1;
+ while (1) {
+ lastxchg = 0;
+ for (i = 0; i < limit; i++) {
+ if ((*compare)(primary[i], primary[i + 1]) > 0) {
+ /* exchange the neighbours */
+ tmp = primary[i + 1];
+ primary[i + 1] = primary[i];
+ primary[i] = tmp;
+ if (secondary) { /* secondary array? */
+ tmp = secondary[i + 1]; /* exchange there as well */
+ secondary[i + 1] = secondary[i];
+ secondary[i] = tmp;
+ }
+ if (tertiary) { /* tertiary array? */
+ tmp = tertiary[i + 1]; /* exchange there as well */
+ tertiary[i + 1] = tertiary[i];
+ tertiary[i] = tmp;
+ }
+ lastxchg = i + 1; /* index of the last element bubbled up */
+ }
+ }
+ if (lastxchg == 0) /* no exchanges, so array is sorted */
+ break; /* we're done */
+ limit = lastxchg; /* array is sorted up to [limit] */
+ }
+}
+
+#if 0
+
+/* array iterator stuff... not used */
+
+typedef void **NSSCMSArrayIterator;
+
+/* iterator */
+NSSCMSArrayIterator
+NSS_CMSArray_First(void **array)
+{
+ if (array == NULL || array[0] == NULL)
+ return NULL;
+ return (NSSCMSArrayIterator)&(array[0]);
+}
+
+void *
+NSS_CMSArray_Obj(NSSCMSArrayIterator iter)
+{
+ void **p = (void **)iter;
+
+ return *iter; /* which is NULL if we are at the end of the array */
+}
+
+NSSCMSArrayIterator
+NSS_CMSArray_Next(NSSCMSArrayIterator iter)
+{
+ void **p = (void **)iter;
+
+ return (NSSCMSArrayIterator)(p + 1);
+}
+
+#endif
diff --git a/security/nss/lib/smime/cmsasn1.c b/security/nss/lib/smime/cmsasn1.c
new file mode 100644
index 0000000000..8ba95d044e
--- /dev/null
+++ b/security/nss/lib/smime/cmsasn1.c
@@ -0,0 +1,491 @@
+/* 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 ASN.1 templates
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "prtime.h"
+#include "secerr.h"
+
+extern const SEC_ASN1Template nss_cms_set_of_attribute_template[];
+
+SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
+SEC_ASN1_MKSUB(CERT_SetOfSignedCrlTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToOctetStringTemplate)
+SEC_ASN1_MKSUB(SEC_SetOfAnyTemplate)
+
+/* -----------------------------------------------------------------------------
+ * MESSAGE
+ * (uses NSSCMSContentInfo)
+ */
+
+/* forward declaration */
+static const SEC_ASN1Template *
+nss_cms_choose_content_template(void *src_or_dest, PRBool encoding);
+
+static const SEC_ASN1TemplateChooserPtr nss_cms_chooser = nss_cms_choose_content_template;
+
+const SEC_ASN1Template NSSCMSMessageTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSMessage) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(NSSCMSMessage, contentInfo.contentType) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSCMSMessage, contentInfo.content),
+ &nss_cms_chooser },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * ENCAPSULATED & ENCRYPTED CONTENTINFO
+ * (both use a NSSCMSContentInfo)
+ */
+static const SEC_ASN1Template NSSCMSEncapsulatedContentInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSContentInfo) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(NSSCMSContentInfo, contentType) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_MAY_STREAM |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSContentInfo, rawContent),
+ SEC_ASN1_SUB(SEC_PointerToOctetStringTemplate) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSEncryptedContentInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSContentInfo) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(NSSCMSContentInfo, contentType) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSContentInfo, contentEncAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM |
+ SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSContentInfo, rawContent),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * SIGNED DATA
+ */
+
+const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
+
+const SEC_ASN1Template NSSCMSSignedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSSignedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSSignedData, version) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
+ offsetof(NSSCMSSignedData, digestAlgorithms),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSSignedData, contentInfo),
+ NSSCMSEncapsulatedContentInfoTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSSignedData, rawCerts),
+ SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(NSSCMSSignedData, crls),
+ SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
+ { SEC_ASN1_SET_OF,
+ offsetof(NSSCMSSignedData, signerInfos),
+ NSSCMSSignerInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template NSS_PointerToCMSSignedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, NSSCMSSignedDataTemplate }
+};
+
+/* -----------------------------------------------------------------------------
+ * signeridentifier
+ */
+
+static const SEC_ASN1Template NSSCMSSignerIdentifierTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSCMSSignerIdentifier, identifierType), NULL,
+ sizeof(NSSCMSSignerIdentifier) },
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSSignerIdentifier, id.subjectKeyID),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate),
+ NSSCMSRecipientID_SubjectKeyID },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+ offsetof(NSSCMSSignerIdentifier, id.issuerAndSN),
+ SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
+ NSSCMSRecipientID_IssuerSN },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * signerinfo
+ */
+
+const SEC_ASN1Template NSSCMSSignerInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSSignerInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSSignerInfo, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSSignerInfo, signerIdentifier),
+ NSSCMSSignerIdentifierTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSSignerInfo, digestAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSCMSSignerInfo, authAttr),
+ nss_cms_set_of_attribute_template },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSSignerInfo, digestEncAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSSignerInfo, encDigest) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(NSSCMSSignerInfo, unAuthAttr),
+ nss_cms_set_of_attribute_template },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * ENVELOPED DATA
+ */
+
+static const SEC_ASN1Template NSSCMSOriginatorInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSOriginatorInfo) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSOriginatorInfo, rawCerts),
+ SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(NSSCMSOriginatorInfo, crls),
+ SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
+
+const SEC_ASN1Template NSSCMSEnvelopedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSEnvelopedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSEnvelopedData, version) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSCMSEnvelopedData, originatorInfo),
+ NSSCMSOriginatorInfoTemplate },
+ { SEC_ASN1_SET_OF,
+ offsetof(NSSCMSEnvelopedData, recipientInfos),
+ NSSCMSRecipientInfoTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSEnvelopedData, contentInfo),
+ NSSCMSEncryptedContentInfoTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(NSSCMSEnvelopedData, unprotectedAttr),
+ nss_cms_set_of_attribute_template },
+ { 0 }
+};
+
+const SEC_ASN1Template NSS_PointerToCMSEnvelopedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, NSSCMSEnvelopedDataTemplate }
+};
+
+/* here come the 15 gazillion templates for all the v3 varieties of RecipientInfo */
+
+/* -----------------------------------------------------------------------------
+ * key transport recipient info
+ */
+
+static const SEC_ASN1Template NSSCMSRecipientIdentifierTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSCMSRecipientIdentifier, identifierType), NULL,
+ sizeof(NSSCMSRecipientIdentifier) },
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(NSSCMSRecipientIdentifier, id.subjectKeyID),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate),
+ NSSCMSRecipientID_SubjectKeyID },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+ offsetof(NSSCMSRecipientIdentifier, id.issuerAndSN),
+ SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
+ NSSCMSRecipientID_IssuerSN },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSKeyTransRecipientInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSKeyTransRecipientInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSKeyTransRecipientInfo, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSKeyTransRecipientInfo, recipientIdentifier),
+ NSSCMSRecipientIdentifierTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSKeyTransRecipientInfo, keyEncAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSKeyTransRecipientInfo, encKey) },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * key agreement recipient info
+ */
+
+static const SEC_ASN1Template NSSCMSOriginatorPublicKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSOriginatorPublicKey) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSOriginatorPublicKey, algorithmIdentifier),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSOriginatorPublicKey, publicKey),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSOriginatorIdentifierOrKeyTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSCMSOriginatorIdentifierOrKey, identifierType), NULL,
+ sizeof(NSSCMSOriginatorIdentifierOrKey) },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+ offsetof(NSSCMSOriginatorIdentifierOrKey, id.issuerAndSN),
+ SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
+ NSSCMSOriginatorIDOrKey_IssuerSN },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(NSSCMSOriginatorIdentifierOrKey, id.subjectKeyID),
+ SEC_ASN1_SUB(SEC_PointerToOctetStringTemplate),
+ NSSCMSOriginatorIDOrKey_SubjectKeyID },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ offsetof(NSSCMSOriginatorIdentifierOrKey, id.originatorPublicKey),
+ NSSCMSOriginatorPublicKeyTemplate,
+ NSSCMSOriginatorIDOrKey_OriginatorPublicKey },
+ { 0 }
+};
+
+const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSRecipientKeyIdentifier) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSRecipientKeyIdentifier, subjectKeyIdentifier) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSRecipientKeyIdentifier, date) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSRecipientKeyIdentifier, other) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSKeyAgreeRecipientIdentifierTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSCMSKeyAgreeRecipientIdentifier, identifierType), NULL,
+ sizeof(NSSCMSKeyAgreeRecipientIdentifier) },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+ offsetof(NSSCMSKeyAgreeRecipientIdentifier, id.issuerAndSN),
+ SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
+ NSSCMSKeyAgreeRecipientID_IssuerSN },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSCMSKeyAgreeRecipientIdentifier, id.recipientKeyIdentifier),
+ NSSCMSRecipientKeyIdentifierTemplate,
+ NSSCMSKeyAgreeRecipientID_RKeyID },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSRecipientEncryptedKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSRecipientEncryptedKey) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSRecipientEncryptedKey, recipientIdentifier),
+ NSSCMSKeyAgreeRecipientIdentifierTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSRecipientEncryptedKey, encKey),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSKeyAgreeRecipientInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSKeyAgreeRecipientInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSKeyAgreeRecipientInfo, version) },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSCMSKeyAgreeRecipientInfo, originatorIdentifierOrKey),
+ NSSCMSOriginatorIdentifierOrKeyTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+ offsetof(NSSCMSKeyAgreeRecipientInfo, ukm),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSKeyAgreeRecipientInfo, keyEncAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_SEQUENCE_OF,
+ offsetof(NSSCMSKeyAgreeRecipientInfo, recipientEncryptedKeys),
+ NSSCMSRecipientEncryptedKeyTemplate },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * KEK recipient info
+ */
+
+static const SEC_ASN1Template NSSCMSKEKIdentifierTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSKEKIdentifier) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSKEKIdentifier, keyIdentifier) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSKEKIdentifier, date) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSKEKIdentifier, other) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSCMSKEKRecipientInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSKEKRecipientInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSKEKRecipientInfo, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSKEKRecipientInfo, kekIdentifier),
+ NSSCMSKEKIdentifierTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSKEKRecipientInfo, keyEncAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSKEKRecipientInfo, encKey) },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ * recipient info
+ */
+const SEC_ASN1Template NSSCMSRecipientInfoTemplate[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSCMSRecipientInfo, recipientInfoType), NULL,
+ sizeof(NSSCMSRecipientInfo) },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(NSSCMSRecipientInfo, ri.keyAgreeRecipientInfo),
+ NSSCMSKeyAgreeRecipientInfoTemplate,
+ NSSCMSRecipientInfoID_KeyAgree },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ offsetof(NSSCMSRecipientInfo, ri.kekRecipientInfo),
+ NSSCMSKEKRecipientInfoTemplate,
+ NSSCMSRecipientInfoID_KEK },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSRecipientInfo, ri.keyTransRecipientInfo),
+ NSSCMSKeyTransRecipientInfoTemplate,
+ NSSCMSRecipientInfoID_KeyTrans },
+ { 0 }
+};
+
+/* -----------------------------------------------------------------------------
+ *
+ */
+
+const SEC_ASN1Template NSSCMSDigestedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSDigestedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSDigestedData, version) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSCMSDigestedData, digestAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSDigestedData, contentInfo),
+ NSSCMSEncapsulatedContentInfoTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSCMSDigestedData, digest) },
+ { 0 }
+};
+
+const SEC_ASN1Template NSS_PointerToCMSDigestedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, NSSCMSDigestedDataTemplate }
+};
+
+const SEC_ASN1Template NSSCMSEncryptedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(NSSCMSEncryptedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSCMSEncryptedData, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSEncryptedData, contentInfo),
+ NSSCMSEncryptedContentInfoTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(NSSCMSEncryptedData, unprotectedAttr),
+ nss_cms_set_of_attribute_template },
+ { 0 }
+};
+
+const SEC_ASN1Template NSS_PointerToCMSEncryptedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, NSSCMSEncryptedDataTemplate }
+};
+
+const SEC_ASN1Template NSSCMSGenericWrapperDataTemplate[] = {
+ { SEC_ASN1_INLINE,
+ offsetof(NSSCMSGenericWrapperData, contentInfo),
+ NSSCMSEncapsulatedContentInfoTemplate },
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(NSSCMSGenericWrapperDataTemplate)
+
+const SEC_ASN1Template NSS_PointerToCMSGenericWrapperDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, NSSCMSGenericWrapperDataTemplate }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(NSS_PointerToCMSGenericWrapperDataTemplate)
+
+/* -----------------------------------------------------------------------------
+ *
+ */
+static const SEC_ASN1Template *
+nss_cms_choose_content_template(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ NSSCMSContentInfo *cinfo;
+ SECOidTag type;
+
+ PORT_Assert(src_or_dest != NULL);
+ if (src_or_dest == NULL)
+ return NULL;
+
+ cinfo = (NSSCMSContentInfo *)src_or_dest;
+ type = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ switch (type) {
+ default:
+ theTemplate = NSS_CMSType_GetTemplate(type);
+ break;
+ case SEC_OID_PKCS7_DATA:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ theTemplate = NSS_PointerToCMSSignedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ theTemplate = NSS_PointerToCMSEnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ theTemplate = NSS_PointerToCMSDigestedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ theTemplate = NSS_PointerToCMSEncryptedDataTemplate;
+ break;
+ }
+ return theTemplate;
+}
diff --git a/security/nss/lib/smime/cmsattr.c b/security/nss/lib/smime/cmsattr.c
new file mode 100644
index 0000000000..c1ec760d24
--- /dev/null
+++ b/security/nss/lib/smime/cmsattr.c
@@ -0,0 +1,425 @@
+/* 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 attributes.
+ */
+
+#include "cmslocal.h"
+
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+/*
+ * -------------------------------------------------------------------
+ * XXX The following Attribute stuff really belongs elsewhere.
+ * The Attribute type is *not* part of CMS but rather X.501.
+ * But for now, since CMS is the only customer of attributes,
+ * we define them here. Once there is a use outside of CMS,
+ * then change the attribute types and functions from internal
+ * to external naming convention, and move them elsewhere!
+ */
+
+/*
+ * NSS_CMSAttribute_Create - create an attribute
+ *
+ * if value is NULL, the attribute won't have a value. It can be added later
+ * with NSS_CMSAttribute_AddValue.
+ */
+NSSCMSAttribute *
+NSS_CMSAttribute_Create(PLArenaPool *poolp, SECOidTag oidtag, SECItem *value,
+ PRBool encoded)
+{
+ NSSCMSAttribute *attr;
+ SECItem *copiedvalue;
+ void *mark;
+
+ PORT_Assert(poolp != NULL);
+
+ mark = PORT_ArenaMark(poolp);
+
+ attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
+ 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;
+
+ if (value != NULL) {
+ if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
+ goto loser;
+
+ if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
+ goto loser;
+ }
+
+ attr->encoded = encoded;
+
+ PORT_ArenaUnmark(poolp, mark);
+
+ return attr;
+
+loser:
+ PORT_Assert(mark != NULL);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/*
+ * NSS_CMSAttribute_AddValue - add another value to an attribute
+ */
+SECStatus
+NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value)
+{
+ SECItem *copiedvalue;
+ void *mark;
+
+ PORT_Assert(poolp != NULL);
+
+ mark = PORT_ArenaMark(poolp);
+
+ if (value == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+
+ if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
+ goto loser;
+
+ if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_Assert(mark != NULL);
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSAttribute_GetType - return the OID tag
+ */
+SECOidTag
+NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
+{
+ SECOidData *typetag;
+
+ typetag = SECOID_FindOID(&(attr->type));
+ if (typetag == NULL)
+ return SEC_OID_UNKNOWN;
+
+ return typetag->offset;
+}
+
+/*
+ * NSS_CMSAttribute_GetValue - return the first attribute value
+ *
+ * We do some sanity checking first:
+ * - Multiple values are *not* expected.
+ * - Empty values are *not* expected.
+ */
+SECItem *
+NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr)
+{
+ SECItem *value;
+
+ if (attr == NULL)
+ return NULL;
+
+ value = attr->values[0];
+
+ if (value == NULL || value->data == NULL || value->len == 0)
+ return NULL;
+
+ if (attr->values[1] != NULL)
+ return NULL;
+
+ return value;
+}
+
+/*
+ * NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
+ */
+PRBool
+NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av)
+{
+ SECItem *value;
+
+ if (attr == NULL)
+ return PR_FALSE;
+
+ value = NSS_CMSAttribute_GetValue(attr);
+
+ return (value != NULL && value->len == av->len &&
+ PORT_Memcmp(value->data, av->data, value->len) == 0);
+}
+
+/*
+ * templates and functions for separate ASN.1 encoding of attributes
+ *
+ * used in NSS_CMSAttributeArray_Reorder
+ */
+
+/*
+ * helper function for dynamic template determination of the attribute value
+ */
+static const SEC_ASN1Template *
+cms_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ NSSCMSAttribute *attribute;
+ SECOidData *oiddata;
+ PRBool encoded;
+
+ PORT_Assert(src_or_dest != NULL);
+ if (src_or_dest == NULL)
+ return NULL;
+
+ attribute = (NSSCMSAttribute *)src_or_dest;
+
+ if (encoding && (!attribute->values || !attribute->values[0] ||
+ attribute->encoded)) {
+ /* we're encoding, and the attribute has no value or the attribute
+ * value is already encoded. */
+ return SEC_ASN1_GET(SEC_AnyTemplate);
+ }
+
+ /* get attribute's typeTag */
+ oiddata = attribute->typeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&attribute->type);
+ attribute->typeTag = oiddata;
+ }
+
+ if (oiddata == NULL) {
+ /* still no OID tag? OID is unknown then. en/decode value as ANY. */
+ encoded = PR_TRUE;
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ } else {
+ switch (oiddata->offset) {
+ case SEC_OID_PKCS9_SMIME_CAPABILITIES:
+ case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
+ /* these guys need to stay DER-encoded */
+ default:
+ /* same goes for OIDs that are not handled here */
+ encoded = PR_TRUE;
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ break;
+ /* otherwise choose proper template */
+ case SEC_OID_PKCS9_EMAIL_ADDRESS:
+ case SEC_OID_RFC1274_MAIL:
+ case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
+ encoded = PR_FALSE;
+ theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
+ break;
+ case SEC_OID_PKCS9_CONTENT_TYPE:
+ encoded = PR_FALSE;
+ theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
+ break;
+ case SEC_OID_PKCS9_MESSAGE_DIGEST:
+ encoded = PR_FALSE;
+ theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
+ break;
+ case SEC_OID_PKCS9_SIGNING_TIME:
+ encoded = PR_FALSE;
+ theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
+ break;
+ /* XXX Want other types here, too */
+ }
+ }
+
+ if (encoding) {
+ /*
+ * If we are encoding and we think we have an already-encoded value,
+ * then the code which initialized this attribute should have set
+ * the "encoded" property to true (and we would have returned early,
+ * up above). No devastating error, but that code should be fixed.
+ * (It could indicate that the resulting encoded bytes are wrong.)
+ */
+ PORT_Assert(!encoded);
+ } else {
+ /*
+ * We are decoding; record whether the resulting value is
+ * still encoded or not.
+ */
+ attribute->encoded = encoded;
+ }
+ return theTemplate;
+}
+
+static const SEC_ASN1TemplateChooserPtr cms_attr_chooser = cms_attr_choose_attr_value_template;
+
+const SEC_ASN1Template nss_cms_attribute_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSCMSAttribute) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(NSSCMSAttribute, type) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
+ offsetof(NSSCMSAttribute, values),
+ &cms_attr_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
+ { SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
+};
+
+/* =============================================================================
+ * Attribute Array methods
+ */
+
+/*
+ * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
+ *
+ * If you are wondering why this routine does not reorder the attributes
+ * first, and might be tempted to make it do so, see the comment by the
+ * call to ReorderAttributes in cmsencode.c. (Or, see who else calls this
+ * and think long and hard about the implications of making it always
+ * do the reordering.)
+ */
+SECItem *
+NSS_CMSAttributeArray_Encode(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
+{
+ return SEC_ASN1EncodeItem(poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
+}
+
+/*
+ * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
+ *
+ * make sure that the order of the attributes guarantees valid DER (which must be
+ * in lexigraphically ascending order for a SET OF); if reordering is necessary it
+ * will be done in place (in attrs).
+ */
+SECStatus
+NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
+{
+ return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
+}
+
+/*
+ * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
+ * find one that matches the specified object ID.
+ *
+ * If "only" is true, then make sure that there is not more than one attribute
+ * of the same type. Otherwise, just return the first one found. (XXX Does
+ * anybody really want that first-found behavior? It was like that when I found it...)
+ */
+NSSCMSAttribute *
+NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
+{
+ SECOidData *oid;
+ NSSCMSAttribute *attr1, *attr2;
+
+ if (attrs == NULL)
+ return NULL;
+
+ oid = SECOID_FindOIDByTag(oidtag);
+ if (oid == NULL)
+ return NULL;
+
+ while ((attr1 = *attrs++) != NULL) {
+ if (attr1->type.len == oid->oid.len &&
+ PORT_Memcmp(attr1->type.data, oid->oid.data, oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr1 == NULL)
+ return NULL;
+
+ if (!only)
+ return attr1;
+
+ while ((attr2 = *attrs++) != NULL) {
+ if (attr2->type.len == oid->oid.len &&
+ PORT_Memcmp(attr2->type.data, oid->oid.data, oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr2 != NULL)
+ return NULL;
+
+ return attr1;
+}
+
+/*
+ * NSS_CMSAttributeArray_AddAttr - add an attribute to an
+ * array of attributes.
+ */
+SECStatus
+NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs,
+ NSSCMSAttribute *attr)
+{
+ NSSCMSAttribute *oattr;
+ void *mark;
+ SECOidTag type;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* find oidtag of attr */
+ type = NSS_CMSAttribute_GetType(attr);
+
+ /* see if we have one already */
+ oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
+ PORT_Assert(oattr == NULL);
+ if (oattr != NULL)
+ goto loser; /* XXX or would it be better to replace it? */
+
+ /* no, shove it in */
+ if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
+ */
+SECStatus
+NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs,
+ SECOidTag type, SECItem *value, PRBool encoded)
+{
+ NSSCMSAttribute *attr;
+ void *mark;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* see if we have one already */
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
+ if (attr == NULL) {
+ /* not found? create one! */
+ attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
+ if (attr == NULL)
+ goto loser;
+ /* and add it to the list */
+ if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
+ goto loser;
+ } else {
+ /* found, shove it in */
+ /* XXX we need a decent memory model @#$#$!#!!! */
+ attr->values[0] = value;
+ attr->encoded = encoded;
+ }
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
diff --git a/security/nss/lib/smime/cmscinfo.c b/security/nss/lib/smime/cmscinfo.c
new file mode 100644
index 0000000000..453ccaadaa
--- /dev/null
+++ b/security/nss/lib/smime/cmscinfo.c
@@ -0,0 +1,443 @@
+/* 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 contentInfo methods.
+ */
+
+#include "cmslocal.h"
+
+#include "pk11func.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secerr.h"
+
+/*
+ * NSS_CMSContentInfo_Create - create a content info
+ *
+ * version is set in the _Finalize procedures for each content type
+ */
+SECStatus
+NSS_CMSContentInfo_Private_Init(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo->privateInfo) {
+ return SECSuccess;
+ }
+ cinfo->privateInfo = PORT_ZNew(NSSCMSContentInfoPrivate);
+ return (cinfo->privateInfo) ? SECSuccess : SECFailure;
+}
+
+static void
+nss_cmsContentInfo_private_destroy(NSSCMSContentInfoPrivate *privateInfo)
+{
+ if (privateInfo->digcx) {
+ /* must destroy digest objects */
+ NSS_CMSDigestContext_Cancel(privateInfo->digcx);
+ privateInfo->digcx = NULL;
+ }
+ if (privateInfo->ciphcx) {
+ NSS_CMSCipherContext_Destroy(privateInfo->ciphcx);
+ privateInfo->ciphcx = NULL;
+ }
+ PORT_Free(privateInfo);
+}
+
+/*
+ * NSS_CMSContentInfo_Destroy - destroy a CMS contentInfo and all of its sub-pieces.
+ */
+void
+NSS_CMSContentInfo_Destroy(NSSCMSContentInfo *cinfo)
+{
+ SECOidTag kind;
+
+ if (cinfo == NULL) {
+ return;
+ }
+
+ kind = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ NSS_CMSEnvelopedData_Destroy(cinfo->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ NSS_CMSSignedData_Destroy(cinfo->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ NSS_CMSEncryptedData_Destroy(cinfo->content.encryptedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ NSS_CMSDigestedData_Destroy(cinfo->content.digestedData);
+ break;
+ default:
+ NSS_CMSGenericWrapperData_Destroy(kind, cinfo->content.genericData);
+ /* XXX Anything else that needs to be "manually" freed/destroyed? */
+ break;
+ }
+ if (cinfo->privateInfo) {
+ nss_cmsContentInfo_private_destroy(cinfo->privateInfo);
+ cinfo->privateInfo = NULL;
+ }
+ if (cinfo->bulkkey) {
+ PK11_FreeSymKey(cinfo->bulkkey);
+ }
+}
+
+/*
+ * NSS_CMSContentInfo_GetChildContentInfo - get content's contentInfo (if it exists)
+ */
+NSSCMSContentInfo *
+NSS_CMSContentInfo_GetChildContentInfo(NSSCMSContentInfo *cinfo)
+{
+ NSSCMSContentInfo *ccinfo = NULL;
+
+ if (cinfo == NULL) {
+ return NULL;
+ }
+
+ SECOidTag tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ switch (tag) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (cinfo->content.signedData != NULL) {
+ ccinfo = &(cinfo->content.signedData->contentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ if (cinfo->content.envelopedData != NULL) {
+ ccinfo = &(cinfo->content.envelopedData->contentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ if (cinfo->content.digestedData != NULL) {
+ ccinfo = &(cinfo->content.digestedData->contentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ if (cinfo->content.encryptedData != NULL) {
+ ccinfo = &(cinfo->content.encryptedData->contentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_DATA:
+ default:
+ if (NSS_CMSType_IsWrapper(tag)) {
+ if (cinfo->content.genericData != NULL) {
+ ccinfo = &(cinfo->content.genericData->contentInfo);
+ }
+ }
+ break;
+ }
+ if (ccinfo && !ccinfo->privateInfo) {
+ NSS_CMSContentInfo_Private_Init(ccinfo);
+ }
+ return ccinfo;
+}
+
+SECStatus
+NSS_CMSContentInfo_SetDontStream(NSSCMSContentInfo *cinfo, PRBool dontStream)
+{
+ SECStatus rv;
+ if (cinfo == NULL) {
+ return SECFailure;
+ }
+
+ rv = NSS_CMSContentInfo_Private_Init(cinfo);
+ if (rv != SECSuccess) {
+ /* default is streaming, failure to get ccinfo will not effect this */
+ return dontStream ? SECFailure : SECSuccess;
+ }
+ cinfo->privateInfo->dontStream = dontStream;
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSContentInfo_SetContent - set content type & content
+ */
+SECStatus
+NSS_CMSContentInfo_SetContent(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ SECOidTag type, void *ptr)
+{
+ SECStatus rv;
+ if (cinfo == NULL || cmsg == NULL) {
+ return SECFailure;
+ }
+
+ cinfo->contentTypeTag = SECOID_FindOIDByTag(type);
+ if (cinfo->contentTypeTag == NULL) {
+ return SECFailure;
+ }
+
+ /* do not copy the oid, just create a reference */
+ rv = SECITEM_CopyItem(cmsg->poolp, &(cinfo->contentType), &(cinfo->contentTypeTag->oid));
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ cinfo->content.pointer = ptr;
+
+ if (NSS_CMSType_IsData(type) && ptr) {
+ cinfo->rawContent = ptr;
+ } else {
+ /* as we always have some inner data,
+ * we need to set it to something, just to fool the encoder enough to work on it
+ * and get us into nss_cms_encoder_notify at that point */
+ cinfo->rawContent = SECITEM_AllocItem(cmsg->poolp, NULL, 1);
+ if (cinfo->rawContent == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSContentInfo_SetContent_XXXX - typesafe wrappers for NSS_CMSContentInfo_SetContent
+ */
+
+/*
+ * data == NULL -> pass in data via NSS_CMSEncoder_Update
+ * data != NULL -> take this data
+ */
+SECStatus
+NSS_CMSContentInfo_SetContent_Data(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ SECItem *data, PRBool detached)
+{
+ if (NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_DATA, (void *)data) != SECSuccess) {
+ return SECFailure;
+ }
+ if (detached) {
+ cinfo->rawContent = NULL;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContent_SignedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ NSSCMSSignedData *sigd)
+{
+ return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_SIGNED_DATA, (void *)sigd);
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContent_EnvelopedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ NSSCMSEnvelopedData *envd)
+{
+ return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_ENVELOPED_DATA, (void *)envd);
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContent_DigestedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ NSSCMSDigestedData *digd)
+{
+ return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_DIGESTED_DATA, (void *)digd);
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContent_EncryptedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo,
+ NSSCMSEncryptedData *encd)
+{
+ return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_ENCRYPTED_DATA, (void *)encd);
+}
+
+/*
+ * NSS_CMSContentInfo_GetContent - get pointer to inner content
+ *
+ * needs to be casted...
+ */
+void *
+NSS_CMSContentInfo_GetContent(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return NULL;
+ }
+
+ SECOidTag tag = cinfo->contentTypeTag
+ ? cinfo->contentTypeTag->offset
+ : SEC_OID_UNKNOWN;
+ switch (tag) {
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ return cinfo->content.pointer;
+ default:
+ return NSS_CMSType_IsWrapper(tag) ? cinfo->content.pointer
+ : (NSS_CMSType_IsData(tag) ? cinfo->rawContent
+ : NULL);
+ }
+}
+
+/*
+ * NSS_CMSContentInfo_GetInnerContent - get pointer to innermost content
+ *
+ * this is typically only called by NSS_CMSMessage_GetContent()
+ */
+
+SECItem *
+NSS_CMSContentInfo_GetInnerContent(NSSCMSContentInfo *cinfo)
+{
+ NSSCMSContentInfo *ccinfo;
+ SECOidTag tag;
+ SECItem *pItem = NULL;
+
+ if (cinfo == NULL) {
+ return NULL;
+ }
+
+ tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ if (NSS_CMSType_IsData(tag)) {
+ pItem = cinfo->content.data;
+ } else if (NSS_CMSType_IsWrapper(tag)) {
+ ccinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo);
+ if (ccinfo != NULL) {
+ pItem = NSS_CMSContentInfo_GetContent(ccinfo);
+ }
+ } else {
+ PORT_Assert(0);
+ }
+
+ return pItem;
+}
+
+/*
+ * NSS_CMSContentInfo_GetContentType{Tag,OID} - find out (saving pointer to lookup result
+ * for future reference) and return the inner content type.
+ */
+SECOidTag
+NSS_CMSContentInfo_GetContentTypeTag(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return SEC_OID_UNKNOWN;
+ }
+
+ if (cinfo->contentTypeTag == NULL)
+ cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+
+ if (cinfo->contentTypeTag == NULL)
+ return SEC_OID_UNKNOWN;
+
+ return cinfo->contentTypeTag->offset;
+}
+
+SECItem *
+NSS_CMSContentInfo_GetContentTypeOID(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return NULL;
+ }
+
+ if (cinfo->contentTypeTag == NULL) {
+ cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+ }
+
+ if (cinfo->contentTypeTag == NULL) {
+ return NULL;
+ }
+
+ return &(cinfo->contentTypeTag->oid);
+}
+
+/*
+ * NSS_CMSContentInfo_GetContentEncAlgTag - find out (saving pointer to lookup result
+ * for future reference) and return the content encryption algorithm tag.
+ */
+SECOidTag
+NSS_CMSContentInfo_GetContentEncAlgTag(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return SEC_OID_UNKNOWN;
+ }
+
+ if (cinfo->contentEncAlgTag == SEC_OID_UNKNOWN) {
+ cinfo->contentEncAlgTag = SECOID_GetAlgorithmTag(&(cinfo->contentEncAlg));
+ }
+
+ return cinfo->contentEncAlgTag;
+}
+
+/*
+ * NSS_CMSContentInfo_GetContentEncAlg - find out and return the content encryption algorithm tag.
+ */
+SECAlgorithmID *
+NSS_CMSContentInfo_GetContentEncAlg(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return NULL;
+ }
+
+ return &(cinfo->contentEncAlg);
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContentEncAlg(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
+ SECOidTag bulkalgtag, SECItem *parameters, int keysize)
+{
+ SECStatus rv;
+ if (cinfo == NULL) {
+ return SECFailure;
+ }
+
+ rv = SECOID_SetAlgorithmID(poolp, &(cinfo->contentEncAlg), bulkalgtag, parameters);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ cinfo->keysize = keysize;
+ return SECSuccess;
+}
+
+SECStatus
+NSS_CMSContentInfo_SetContentEncAlgID(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
+ SECAlgorithmID *algid, int keysize)
+{
+ SECStatus rv;
+ if (cinfo == NULL) {
+ return SECFailure;
+ }
+
+ rv = SECOID_CopyAlgorithmID(poolp, &(cinfo->contentEncAlg), algid);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ if (keysize >= 0) {
+ cinfo->keysize = keysize;
+ }
+ return SECSuccess;
+}
+
+void
+NSS_CMSContentInfo_SetBulkKey(NSSCMSContentInfo *cinfo, PK11SymKey *bulkkey)
+{
+ if (cinfo == NULL) {
+ return;
+ }
+
+ if (bulkkey == NULL) {
+ cinfo->bulkkey = NULL;
+ cinfo->keysize = 0;
+ } else {
+ cinfo->bulkkey = PK11_ReferenceSymKey(bulkkey);
+ cinfo->keysize = PK11_GetKeyStrength(cinfo->bulkkey, &(cinfo->contentEncAlg));
+ }
+}
+
+PK11SymKey *
+NSS_CMSContentInfo_GetBulkKey(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL || cinfo->bulkkey == NULL) {
+ return NULL;
+ }
+
+ return PK11_ReferenceSymKey(cinfo->bulkkey);
+}
+
+int
+NSS_CMSContentInfo_GetBulkKeySize(NSSCMSContentInfo *cinfo)
+{
+ if (cinfo == NULL) {
+ return 0;
+ }
+
+ return cinfo->keysize;
+}
diff --git a/security/nss/lib/smime/cmscipher.c b/security/nss/lib/smime/cmscipher.c
new file mode 100644
index 0000000000..9c8330b235
--- /dev/null
+++ b/security/nss/lib/smime/cmscipher.c
@@ -0,0 +1,722 @@
+/* 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/. */
+
+/*
+ * Encryption/decryption routines for CMS implementation, none of which are exported.
+ */
+
+#include "cmslocal.h"
+
+#include "secoid.h"
+#include "secitem.h"
+#include "pk11func.h"
+#include "secerr.h"
+#include "secpkcs5.h"
+
+/*
+ * -------------------------------------------------------------------
+ * Cipher stuff.
+ */
+
+typedef SECStatus (*nss_cms_cipher_function)(void *, unsigned char *, unsigned int *,
+ unsigned int, const unsigned char *, unsigned int);
+typedef SECStatus (*nss_cms_cipher_destroy)(void *, PRBool);
+
+#define BLOCK_SIZE 4096
+
+struct NSSCMSCipherContextStr {
+ void *cx; /* PK11 cipher context */
+ nss_cms_cipher_function doit;
+ nss_cms_cipher_destroy destroy;
+ PRBool encrypt; /* encrypt / decrypt switch */
+ int block_size; /* block & pad sizes for cipher */
+ int pad_size;
+ int pending_count; /* pending data (not yet en/decrypted */
+ unsigned char pending_buf[BLOCK_SIZE]; /* because of blocking */
+};
+
+/*
+ * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
+ * based on the given bulk encryption key and algorithm identifier (which
+ * may include an iv).
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function below (for starting up encryption) into one routine, and just
+ * have two simple cover functions which call it.
+ */
+NSSCMSCipherContext *
+NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid)
+{
+ NSSCMSCipherContext *cc;
+ void *ciphercx;
+ CK_MECHANISM_TYPE cryptoMechType;
+ PK11SlotInfo *slot;
+ SECOidTag algtag;
+ SECItem *param = NULL;
+
+ algtag = SECOID_GetAlgorithmTag(algid);
+
+ /* set param and mechanism */
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ SECItem *pwitem;
+
+ pwitem = PK11_GetSymKeyUserData(key);
+ if (!pwitem)
+ return NULL;
+
+ cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ SECITEM_FreeItem(param, PR_TRUE);
+ return NULL;
+ }
+
+ } else {
+ cryptoMechType = PK11_AlgtagToMechanism(algtag);
+ if ((param = PK11_ParamFromAlgid(algid)) == NULL)
+ return NULL;
+ }
+
+ cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
+ if (cc == NULL) {
+ SECITEM_FreeItem(param, PR_TRUE);
+ return NULL;
+ }
+
+ /* figure out pad and block sizes */
+ cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
+ slot = PK11_GetSlotFromKey(key);
+ cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
+ PK11_FreeSlot(slot);
+
+ /* create PK11 cipher context */
+ ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
+ key, param);
+ SECITEM_FreeItem(param, PR_TRUE);
+ if (ciphercx == NULL) {
+ PORT_Free(cc);
+ return NULL;
+ }
+
+ cc->cx = ciphercx;
+ cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
+ cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
+ cc->encrypt = PR_FALSE;
+ cc->pending_count = 0;
+
+ return cc;
+}
+
+/*
+ * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
+ * based on the given bulk encryption key and algorithm tag. Fill in the
+ * algorithm identifier (which may include an iv) appropriately.
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function above (for starting up decryption) into one routine, and just
+ * have two simple cover functions which call it.
+ */
+NSSCMSCipherContext *
+NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid)
+{
+ NSSCMSCipherContext *cc;
+ void *ciphercx = NULL;
+ SECStatus rv;
+ CK_MECHANISM_TYPE cryptoMechType;
+ PK11SlotInfo *slot;
+ SECItem *param = NULL;
+ PRBool needToEncodeAlgid = PR_FALSE;
+ SECOidTag algtag = SECOID_GetAlgorithmTag(algid);
+
+ /* set param and mechanism */
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ SECItem *pwitem;
+
+ pwitem = PK11_GetSymKeyUserData(key);
+ if (!pwitem)
+ return NULL;
+
+ cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ SECITEM_FreeItem(param, PR_TRUE);
+ return NULL;
+ }
+ } else {
+ cryptoMechType = PK11_AlgtagToMechanism(algtag);
+ if ((param = PK11_GenerateNewParam(cryptoMechType, key)) == NULL)
+ return NULL;
+ needToEncodeAlgid = PR_TRUE;
+ }
+
+ cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
+ if (cc == NULL) {
+ goto loser;
+ }
+
+ /* now find pad and block sizes for our mechanism */
+ cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
+ slot = PK11_GetSlotFromKey(key);
+ cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
+ PK11_FreeSlot(slot);
+
+ /* and here we go, creating a PK11 cipher context */
+ ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
+ key, param);
+ if (ciphercx == NULL) {
+ PORT_Free(cc);
+ cc = NULL;
+ goto loser;
+ }
+
+ /*
+ * These are placed after the CreateContextBySymKey() because some
+ * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
+ * Don't move it from here.
+ * XXX is that right? the purpose of this is to get the correct algid
+ * containing the IVs etc. for encoding. this means we need to set this up
+ * BEFORE encoding the algid in the contentInfo, right?
+ */
+ if (needToEncodeAlgid) {
+ rv = PK11_ParamToAlgid(algtag, param, poolp, algid);
+ if (rv != SECSuccess) {
+ PORT_Free(cc);
+ cc = NULL;
+ goto loser;
+ }
+ }
+
+ cc->cx = ciphercx;
+ ciphercx = NULL;
+ cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
+ cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
+ cc->encrypt = PR_TRUE;
+ cc->pending_count = 0;
+
+loser:
+ SECITEM_FreeItem(param, PR_TRUE);
+ if (ciphercx) {
+ PK11_DestroyContext(ciphercx, PR_TRUE);
+ }
+
+ return cc;
+}
+
+void
+NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc)
+{
+ PORT_Assert(cc != NULL);
+ if (cc == NULL)
+ return;
+ (*cc->destroy)(cc->cx, PR_TRUE);
+ PORT_Free(cc);
+}
+
+/*
+ * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
+ *
+ * cc - the cipher context
+ * input_len - number of bytes used as input
+ * final - true if this is the final chunk of data
+ *
+ * Result can be used to perform memory allocations. Note that the amount
+ * is exactly accurate only when not doing a block cipher or when final
+ * is false, otherwise it is an upper bound on the amount because until
+ * we see the data we do not know how many padding bytes there are
+ * (always between 1 and bsize).
+ *
+ * Note that this can return zero, which does not mean that the decrypt
+ * operation can be skipped! (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.) However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent decrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
+{
+ int blocks, block_size;
+
+ PORT_Assert(!cc->encrypt);
+
+ block_size = cc->block_size;
+
+ /*
+ * If this is not a block cipher, then we always have the same
+ * number of output bytes as we had input bytes.
+ */
+ if (block_size == 0)
+ return input_len;
+
+ /*
+ * On the final call, we will always use up all of the pending
+ * bytes plus all of the input bytes, *but*, there will be padding
+ * at the end and we cannot predict how many bytes of padding we
+ * will end up removing. The amount given here is actually known
+ * to be at least 1 byte too long (because we know we will have
+ * at least 1 byte of padding), but seemed clearer/better to me.
+ */
+ if (final)
+ return cc->pending_count + input_len;
+
+ /*
+ * Okay, this amount is exactly what we will output on the
+ * next cipher operation. We will always hang onto the last
+ * 1 - block_size bytes for non-final operations. That is,
+ * we will do as many complete blocks as we can *except* the
+ * last block (complete or partial). (This is because until
+ * we know we are at the end, we cannot know when to interpret
+ * and removing the padding byte(s), which are guaranteed to
+ * be there.)
+ */
+ blocks = (cc->pending_count + input_len - 1) / block_size;
+ return blocks * block_size;
+}
+
+/*
+ * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
+ *
+ * cc - the cipher context
+ * input_len - number of bytes used as input
+ * final - true if this is the final chunk of data
+ *
+ * Result can be used to perform memory allocations.
+ *
+ * Note that this can return zero, which does not mean that the encrypt
+ * operation can be skipped! (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.) However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent encrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
+{
+ int blocks, block_size;
+ int pad_size;
+
+ PORT_Assert(cc->encrypt);
+
+ block_size = cc->block_size;
+ pad_size = cc->pad_size;
+
+ /*
+ * If this is not a block cipher, then we always have the same
+ * number of output bytes as we had input bytes.
+ */
+ if (block_size == 0)
+ return input_len;
+
+ /*
+ * On the final call, we only send out what we need for
+ * remaining bytes plus the padding. (There is always padding,
+ * so even if we have an exact number of blocks as input, we
+ * will add another full block that is just padding.)
+ */
+ if (final) {
+ if (pad_size == 0) {
+ return cc->pending_count + input_len;
+ } else {
+ blocks = (cc->pending_count + input_len) / pad_size;
+ blocks++;
+ return blocks * pad_size;
+ }
+ }
+
+ /*
+ * Now, count the number of complete blocks of data we have.
+ */
+ blocks = (cc->pending_count + input_len) / block_size;
+
+ return blocks * block_size;
+}
+
+/*
+ * NSS_CMSCipherContext_Decrypt - do the decryption
+ *
+ * cc - the cipher context
+ * output - buffer for decrypted result bytes
+ * output_len_p - number of bytes in output
+ * max_output_len - upper bound on bytes to put into output
+ * input - pointer to input bytes
+ * input_len - number of input bytes
+ * final - true if this is the final chunk of data
+ *
+ * Decrypts a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the decrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "cc" is the return value from NSS_CMSCipher_StartDecrypt.
+ * When "final" is true, this is the last of the data to be decrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the decryption function will only
+ * operate on whole blocks. But our caller is operating stream-wise,
+ * and can pass in any number of bytes. So we need to keep track
+ * of block boundaries. We save excess bytes between calls in "cc".
+ * We also need to determine which bytes are padding, and remove
+ * them from the output. We can only do this step when we know we
+ * have the final block of data. PKCS #7 specifies that the padding
+ * used for a block cipher is a string of bytes, each of whose value is
+ * the same as the length of the padding, and that all data is padded.
+ * (Even data that starts out with an exact multiple of blocks gets
+ * added to it another block, all of which is padding.)
+ */
+SECStatus
+NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final)
+{
+ unsigned int blocks, bsize, pcount, padsize;
+ unsigned int max_needed, ifraglen, ofraglen, output_len;
+ unsigned char *pbuf;
+ SECStatus rv;
+
+ PORT_Assert(!cc->encrypt);
+
+ /*
+ * Check that we have enough room for the output. Our caller should
+ * already handle this; failure is really an internal error (i.e. bug).
+ */
+ max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final);
+ PORT_Assert(max_output_len >= max_needed);
+ if (max_output_len < max_needed) {
+ /* PORT_SetError (XXX); */
+ return SECFailure;
+ }
+
+ /*
+ * hardware encryption does not like small decryption sizes here, so we
+ * allow both blocking and padding.
+ */
+ bsize = cc->block_size;
+ padsize = cc->pad_size;
+
+ /*
+ * When no blocking or padding work to do, we can simply call the
+ * cipher function and we are done.
+ */
+ if (bsize == 0) {
+ return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
+ input, input_len);
+ }
+
+ pcount = cc->pending_count;
+ pbuf = cc->pending_buf;
+
+ output_len = 0;
+
+ if (pcount) {
+ /*
+ * Try to fill in an entire block, starting with the bytes
+ * we already have saved away.
+ */
+ while (input_len && pcount < bsize) {
+ pbuf[pcount++] = *input++;
+ input_len--;
+ }
+ /*
+ * If we have at most a whole block and this is not our last call,
+ * then we are done for now. (We do not try to decrypt a lone
+ * single block because we cannot interpret the padding bytes
+ * until we know we are handling the very last block of all input.)
+ */
+ if (input_len == 0 && !final) {
+ cc->pending_count = pcount;
+ if (output_len_p)
+ *output_len_p = 0;
+ return SECSuccess;
+ }
+ /*
+ * Given the logic above, we expect to have a full block by now.
+ * If we do not, there is something wrong, either with our own
+ * logic or with (length of) the data given to us.
+ */
+ if ((padsize != 0) && (pcount % padsize) != 0) {
+ PORT_Assert(final);
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ /*
+ * Decrypt the block.
+ */
+ rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then NSS_CMSCipherContext_DecryptLength needs to be made smarter!
+ */
+ PORT_Assert(ofraglen == pcount);
+
+ /*
+ * Account for the bytes now in output.
+ */
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+ }
+
+ /*
+ * If this is our last call, we expect to have an exact number of
+ * blocks left to be decrypted; we will decrypt them all.
+ *
+ * If not our last call, we always save between 1 and bsize bytes
+ * until next time. (We must do this because we cannot be sure
+ * that none of the decrypted bytes are padding bytes until we
+ * have at least another whole block of data. You cannot tell by
+ * looking -- the data could be anything -- you can only tell by
+ * context, knowing you are looking at the last block.) We could
+ * decrypt a whole block now but it is easier if we just treat it
+ * the same way we treat partial block bytes.
+ */
+ if (final) {
+ if (padsize) {
+ blocks = input_len / padsize;
+ ifraglen = blocks * padsize;
+ } else
+ ifraglen = input_len;
+ PORT_Assert(ifraglen == input_len);
+
+ if (ifraglen != input_len) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ } else {
+ blocks = (input_len - 1) / bsize;
+ ifraglen = blocks * bsize;
+ PORT_Assert(ifraglen < input_len);
+
+ pcount = input_len - ifraglen;
+ PORT_Memcpy(pbuf, input + ifraglen, pcount);
+ cc->pending_count = pcount;
+ }
+
+ if (ifraglen) {
+ rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
+ input, ifraglen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7DecryptLength needs to be made smarter!
+ */
+ PORT_Assert(ifraglen == ofraglen);
+ if (ifraglen != ofraglen) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ output_len += ofraglen;
+ } else {
+ ofraglen = 0;
+ }
+
+ /*
+ * If we just did our very last block, "remove" the padding by
+ * adjusting the output length.
+ */
+ if (final && (padsize != 0)) {
+ unsigned int padlen = *(output + ofraglen - 1);
+
+ if (padlen == 0 || padlen > padsize) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ output_len -= padlen;
+ }
+
+ PORT_Assert(output_len_p != NULL || output_len == 0);
+ if (output_len_p != NULL)
+ *output_len_p = output_len;
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSCipherContext_Encrypt - do the encryption
+ *
+ * cc - the cipher context
+ * output - buffer for decrypted result bytes
+ * output_len_p - number of bytes in output
+ * max_output_len - upper bound on bytes to put into output
+ * input - pointer to input bytes
+ * input_len - number of input bytes
+ * final - true if this is the final chunk of data
+ *
+ * Encrypts a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the encrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "cc" is the return value from NSS_CMSCipher_StartEncrypt.
+ * When "final" is true, this is the last of the data to be encrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the encryption function will only
+ * operate on whole blocks. But our caller is operating stream-wise,
+ * and can pass in any number of bytes. So we need to keep track
+ * of block boundaries. We save excess bytes between calls in "cc".
+ * We also need to add padding bytes at the end. PKCS #7 specifies
+ * that the padding used for a block cipher is a string of bytes,
+ * each of whose value is the same as the length of the padding,
+ * and that all data is padded. (Even data that starts out with
+ * an exact multiple of blocks gets added to it another block,
+ * all of which is padding.)
+ *
+ * XXX I would kind of like to combine this with the function above
+ * which does decryption, since they have a lot in common. But the
+ * tricky parts about padding and filling blocks would be much
+ * harder to read that way, so I left them separate. At least for
+ * now until it is clear that they are right.
+ */
+SECStatus
+NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final)
+{
+ int blocks, bsize, padlen, pcount, padsize;
+ unsigned int max_needed, ifraglen, ofraglen, output_len;
+ unsigned char *pbuf;
+ SECStatus rv;
+
+ PORT_Assert(cc->encrypt);
+
+ /*
+ * Check that we have enough room for the output. Our caller should
+ * already handle this; failure is really an internal error (i.e. bug).
+ */
+ max_needed = NSS_CMSCipherContext_EncryptLength(cc, input_len, final);
+ PORT_Assert(max_output_len >= max_needed);
+ if (max_output_len < max_needed) {
+ /* PORT_SetError (XXX); */
+ return SECFailure;
+ }
+
+ bsize = cc->block_size;
+ padsize = cc->pad_size;
+
+ /*
+ * When no blocking and padding work to do, we can simply call the
+ * cipher function and we are done.
+ */
+ if (bsize == 0) {
+ return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
+ input, input_len);
+ }
+
+ pcount = cc->pending_count;
+ pbuf = cc->pending_buf;
+
+ output_len = 0;
+
+ if (pcount) {
+ /*
+ * Try to fill in an entire block, starting with the bytes
+ * we already have saved away.
+ */
+ while (input_len && pcount < bsize) {
+ pbuf[pcount++] = *input++;
+ input_len--;
+ }
+ /*
+ * If we do not have a full block and we know we will be
+ * called again, then we are done for now.
+ */
+ if (pcount < bsize && !final) {
+ cc->pending_count = pcount;
+ if (output_len_p != NULL)
+ *output_len_p = 0;
+ return SECSuccess;
+ }
+ /*
+ * If we have a whole block available, encrypt it.
+ */
+ if ((padsize == 0) || (pcount % padsize) == 0) {
+ rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert(ofraglen == pcount);
+
+ /*
+ * Account for the bytes now in output.
+ */
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+
+ pcount = 0;
+ }
+ }
+
+ if (input_len) {
+ PORT_Assert(pcount == 0);
+
+ blocks = input_len / bsize;
+ ifraglen = blocks * bsize;
+
+ if (ifraglen) {
+ rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
+ input, ifraglen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert(ifraglen == ofraglen);
+
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+ }
+
+ pcount = input_len - ifraglen;
+ PORT_Assert(pcount < bsize);
+ if (pcount)
+ PORT_Memcpy(pbuf, input + ifraglen, pcount);
+ }
+
+ if (final) {
+ if (padsize <= 0) {
+ padlen = 0;
+ } else {
+ padlen = padsize - (pcount % padsize);
+ PORT_Memset(pbuf + pcount, padlen, padlen);
+ }
+ rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount + padlen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert(ofraglen == (pcount + padlen));
+ output_len += ofraglen;
+ } else {
+ cc->pending_count = pcount;
+ }
+
+ PORT_Assert(output_len_p != NULL || output_len == 0);
+ if (output_len_p != NULL)
+ *output_len_p = output_len;
+
+ return SECSuccess;
+}
diff --git a/security/nss/lib/smime/cmsdecode.c b/security/nss/lib/smime/cmsdecode.c
new file mode 100644
index 0000000000..e478979840
--- /dev/null
+++ b/security/nss/lib/smime/cmsdecode.c
@@ -0,0 +1,736 @@
+/* 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 decoding.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "prtime.h"
+#include "secerr.h"
+
+struct NSSCMSDecoderContextStr {
+ SEC_ASN1DecoderContext *dcx; /* ASN.1 decoder context */
+ NSSCMSMessage *cmsg; /* backpointer to the root message */
+ SECOidTag type; /* type of message */
+ NSSCMSContent content; /* pointer to message */
+ NSSCMSDecoderContext *childp7dcx; /* inner CMS decoder context */
+ PRBool saw_contents;
+ int error;
+ NSSCMSContentCallback cb;
+ void *cb_arg;
+ PRBool first_decoded;
+ PRBool need_indefinite_finish;
+};
+
+struct NSSCMSDecoderDataStr {
+ SECItem data; /* must be first */
+ unsigned int totalBufferSize;
+};
+
+typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData;
+
+static void nss_cms_decoder_update_filter(void *arg, const char *data,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind);
+static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx);
+static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx);
+static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx);
+static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
+ const unsigned char *data,
+ unsigned long len,
+ PRBool final);
+static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp);
+
+extern const SEC_ASN1Template NSSCMSMessageTemplate[];
+
+static NSSCMSDecoderData *
+nss_cms_create_decoder_data(PLArenaPool *poolp)
+{
+ NSSCMSDecoderData *decoderData = NULL;
+
+ decoderData = (NSSCMSDecoderData *)
+ PORT_ArenaAlloc(poolp, sizeof(NSSCMSDecoderData));
+ if (!decoderData) {
+ return NULL;
+ }
+ decoderData->data.data = NULL;
+ decoderData->data.len = 0;
+ decoderData->totalBufferSize = 0;
+ return decoderData;
+}
+
+/*
+ * nss_cms_decoder_notify -
+ * this is the driver of the decoding process. It gets called by the ASN.1
+ * decoder before and after an object is decoded.
+ * at various points in the decoding process, we intercept to set up and do
+ * further processing.
+ */
+static void
+nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
+{
+ NSSCMSDecoderContext *p7dcx;
+ NSSCMSContentInfo *rootcinfo, *cinfo;
+ PRBool after = !before;
+
+ p7dcx = (NSSCMSDecoderContext *)arg;
+ rootcinfo = &(p7dcx->cmsg->contentInfo);
+
+ /* XXX error handling: need to set p7dcx->error */
+
+#ifdef CMSDEBUG
+ fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after",
+ dest, depth);
+#endif
+
+ /* so what are we working on right now? */
+ if (p7dcx->type == SEC_OID_UNKNOWN) {
+ /*
+ * right now, we are still decoding the OUTER (root) cinfo
+ * As soon as we know the inner content type, set up the info,
+ * but NO inner decoder or filter. The root decoder handles the first
+ * level children by itself - only for encapsulated contents (which
+ * are encoded as DER inside of an OCTET STRING) we need to set up a
+ * child decoder...
+ */
+ if (after && dest == &(rootcinfo->contentType)) {
+ p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
+ p7dcx->content = rootcinfo->content;
+ /* is this ready already ? need to alloc? */
+ /* XXX yes we need to alloc -- continue here */
+ }
+ } else if (NSS_CMSType_IsData(p7dcx->type)) {
+ /* this can only happen if the outermost cinfo has DATA in it */
+ /* otherwise, we handle this type implicitely in the inner decoders */
+
+ if (before && dest == &(rootcinfo->content)) {
+ /* cause the filter to put the data in the right place...
+ ** We want the ASN.1 decoder to deliver the decoded bytes to us
+ ** from now on
+ */
+ SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
+ nss_cms_decoder_update_filter,
+ p7dcx,
+ (PRBool)(p7dcx->cb != NULL));
+ } else if (after && dest == &(rootcinfo->content.data)) {
+ /* remove the filter */
+ SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
+ }
+ } else if (NSS_CMSType_IsWrapper(p7dcx->type)) {
+ if (!before || dest != &(rootcinfo->content)) {
+
+ if (p7dcx->content.pointer == NULL)
+ p7dcx->content = rootcinfo->content;
+
+ /* get this data type's inner contentInfo */
+ cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer,
+ p7dcx->type);
+
+ if (before && dest == &(cinfo->contentType)) {
+ /* at this point, set up the &%$&$ back pointer */
+ /* we cannot do it later, because the content itself
+ * is optional! */
+ switch (p7dcx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ p7dcx->content.signedData->cmsg = p7dcx->cmsg;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
+ break;
+ default:
+ p7dcx->content.genericData->cmsg = p7dcx->cmsg;
+ break;
+ }
+ }
+
+ if (before && dest == &(cinfo->rawContent)) {
+ /* we want the ASN.1 decoder to deliver the decoded bytes to us
+ ** from now on
+ */
+ SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
+ nss_cms_decoder_update_filter,
+ p7dcx, (PRBool)(p7dcx->cb != NULL));
+
+ /* we're right in front of the data */
+ if (nss_cms_before_data(p7dcx) != SECSuccess) {
+ SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
+ /* stop all processing */
+ p7dcx->error = PORT_GetError();
+ }
+ }
+ if (after && dest == &(cinfo->rawContent)) {
+ /* we're right after of the data */
+ if (nss_cms_after_data(p7dcx) != SECSuccess)
+ p7dcx->error = PORT_GetError();
+
+ /* we don't need to see the contents anymore */
+ SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
+ }
+ }
+ } else {
+ /* unsupported or unknown message type - fail gracefully */
+ p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
+ }
+}
+
+/*
+ * nss_cms_before_data - set up the current encoder to receive data
+ */
+static SECStatus
+nss_cms_before_data(NSSCMSDecoderContext *p7dcx)
+{
+ SECStatus rv;
+ SECOidTag childtype;
+ PLArenaPool *poolp;
+ NSSCMSDecoderContext *childp7dcx;
+ NSSCMSContentInfo *cinfo;
+ const SEC_ASN1Template *template;
+ void *mark = NULL;
+ size_t size;
+
+ poolp = p7dcx->cmsg->poolp;
+
+ /* call _Decode_BeforeData handlers */
+ switch (p7dcx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ /* we're decoding a signedData, so set up the digests */
+ rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ /* we're encoding a digestedData, so set up the digest */
+ rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Decode_BeforeData(
+ p7dcx->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Decode_BeforeData(
+ p7dcx->content.encryptedData);
+ break;
+ default:
+ rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type,
+ p7dcx->content.genericData);
+ }
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ /* ok, now we have a pointer to cinfo */
+ /* find out what kind of data is encapsulated */
+
+ cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
+ childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ if (NSS_CMSType_IsData(childtype)) {
+ cinfo->content.pointer = (void *)nss_cms_create_decoder_data(poolp);
+ if (cinfo->content.pointer == NULL)
+ /* set memory error */
+ return SECFailure;
+
+ p7dcx->childp7dcx = NULL;
+ return SECSuccess;
+ }
+
+ /* set up inner decoder */
+
+ if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL)
+ return SECFailure;
+
+ childp7dcx = PORT_ZNew(NSSCMSDecoderContext);
+ if (childp7dcx == NULL)
+ return SECFailure;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* allocate space for the stuff we're creating */
+ size = NSS_CMSUtil_GetSizeByTypeTag(childtype);
+ childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
+ if (childp7dcx->content.pointer == NULL)
+ goto loser;
+
+ /* give the parent a copy of the pointer so that it doesn't get lost */
+ cinfo->content.pointer = childp7dcx->content.pointer;
+
+ /* start the child decoder */
+ childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer,
+ template);
+ if (childp7dcx->dcx == NULL)
+ goto loser;
+
+ /* the new decoder needs to notify, too */
+ SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify,
+ childp7dcx);
+
+ /* tell the parent decoder that it needs to feed us the content data */
+ p7dcx->childp7dcx = childp7dcx;
+
+ childp7dcx->type = childtype; /* our type */
+
+ childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */
+
+ /* should the child decoder encounter real data,
+ ** it must give it to the caller
+ */
+ childp7dcx->cb = p7dcx->cb;
+ childp7dcx->cb_arg = p7dcx->cb_arg;
+ childp7dcx->first_decoded = PR_FALSE;
+ childp7dcx->need_indefinite_finish = PR_FALSE;
+ if (childtype == SEC_OID_PKCS7_SIGNED_DATA) {
+ childp7dcx->first_decoded = PR_TRUE;
+ }
+
+ /* now set up the parent to hand decoded data to the next level decoder */
+ p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update;
+ p7dcx->cb_arg = childp7dcx;
+
+ PORT_ArenaUnmark(poolp, mark);
+
+ return SECSuccess;
+
+loser:
+ if (mark)
+ PORT_ArenaRelease(poolp, mark);
+ PORT_Free(childp7dcx);
+ p7dcx->childp7dcx = NULL;
+ return SECFailure;
+}
+
+static SECStatus
+nss_cms_after_data(NSSCMSDecoderContext *p7dcx)
+{
+ NSSCMSDecoderContext *childp7dcx;
+ SECStatus rv = SECFailure;
+
+ /* Handle last block. This is necessary to flush out the last bytes
+ * of a possibly incomplete block */
+ nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
+
+ /* finish any "inner" decoders - there's no more data coming... */
+ if (p7dcx->childp7dcx != NULL) {
+ childp7dcx = p7dcx->childp7dcx;
+ if (childp7dcx->dcx != NULL) {
+ /* we started and indefinite sequence somewhere, not complete it */
+ if (childp7dcx->need_indefinite_finish) {
+ static const char lbuf[2] = { 0, 0 };
+ NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf));
+ childp7dcx->need_indefinite_finish = PR_FALSE;
+ }
+
+ if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
+ /* do what? free content? */
+ rv = SECFailure;
+ } else {
+ rv = nss_cms_after_end(childp7dcx);
+ }
+ if (rv != SECSuccess)
+ goto done;
+ }
+ PORT_Free(p7dcx->childp7dcx);
+ p7dcx->childp7dcx = NULL;
+ }
+
+ switch (p7dcx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ /* this will finish the digests and verify */
+ rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Decode_AfterData(
+ p7dcx->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ rv = NSS_CMSDigestedData_Decode_AfterData(
+ p7dcx->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Decode_AfterData(
+ p7dcx->content.encryptedData);
+ break;
+ case SEC_OID_PKCS7_DATA:
+ /* do nothing */
+ break;
+ default:
+ rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type,
+ p7dcx->content.genericData);
+ break;
+ }
+done:
+ return rv;
+}
+
+static SECStatus
+nss_cms_after_end(NSSCMSDecoderContext *p7dcx)
+{
+ SECStatus rv = SECSuccess;
+
+ switch (p7dcx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (p7dcx->content.signedData)
+ rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ if (p7dcx->content.envelopedData)
+ rv = NSS_CMSEnvelopedData_Decode_AfterEnd(
+ p7dcx->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ if (p7dcx->content.digestedData)
+ rv = NSS_CMSDigestedData_Decode_AfterEnd(
+ p7dcx->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ if (p7dcx->content.encryptedData)
+ rv = NSS_CMSEncryptedData_Decode_AfterEnd(
+ p7dcx->content.encryptedData);
+ break;
+ case SEC_OID_PKCS7_DATA:
+ break;
+ default:
+ rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type,
+ p7dcx->content.genericData);
+ break;
+ }
+ return rv;
+}
+
+/*
+ * nss_cms_decoder_work_data - handle decoded data bytes.
+ *
+ * This function either decrypts the data if needed, and/or calculates digests
+ * on it, then either stores it or passes it on to the next level decoder.
+ */
+static void
+nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
+ const unsigned char *data, unsigned long len,
+ PRBool final)
+{
+ NSSCMSContentInfo *cinfo;
+ unsigned char *buf = NULL;
+ unsigned char *dest;
+ unsigned int offset;
+ SECStatus rv;
+
+ /*
+ * We should really have data to process, or we should be trying
+ * to finish/flush the last block. (This is an overly paranoid
+ * check since all callers are in this file and simple inspection
+ * proves they do it right. But it could find a bug in future
+ * modifications/development, that is why it is here.)
+ */
+ PORT_Assert((data != NULL && len) || final);
+
+ cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
+ if (!cinfo) {
+ /* The original programmer didn't expect this to happen */
+ p7dcx->error = SEC_ERROR_LIBRARY_FAILURE;
+ goto loser;
+ }
+
+ if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
+ /*
+ * we are decrypting.
+ *
+ * XXX If we get an error, we do not want to do the digest or callback,
+ * but we want to keep decoding. Or maybe we want to stop decoding
+ * altogether if there is a callback, because obviously we are not
+ * sending the data back and they want to know that.
+ */
+
+ unsigned int outlen = 0; /* length of decrypted data */
+ unsigned int buflen; /* length available for decrypted data */
+
+ /* find out about the length of decrypted data */
+ buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final);
+
+ /*
+ * it might happen that we did not provide enough data for a full
+ * block (decryption unit), and that there is no output available
+ */
+
+ /* no output available, AND no input? */
+ if (buflen == 0 && len == 0)
+ goto loser; /* bail out */
+
+ /*
+ * have inner decoder: pass the data on (means inner content type is NOT data)
+ * no inner decoder: we have DATA in here: either call callback or store
+ */
+ if (buflen != 0) {
+ /* there will be some output - need to make room for it */
+ /* allocate buffer from the heap */
+ buf = (unsigned char *)PORT_Alloc(buflen);
+ if (buf == NULL) {
+ p7dcx->error = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+ }
+
+ /*
+ * decrypt incoming data
+ * buf can still be NULL here (and buflen == 0) here if we don't expect
+ * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to
+ * keep track of incoming data
+ */
+ rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
+ data, len, final);
+ if (rv != SECSuccess) {
+ p7dcx->error = PORT_GetError();
+ goto loser;
+ }
+
+ PORT_Assert(final || outlen == buflen);
+
+ /* swap decrypted data in */
+ data = buf;
+ len = outlen;
+ }
+
+ if (len == 0)
+ goto done; /* nothing more to do */
+
+ /*
+ * Update the running digests with plaintext bytes (if we need to).
+ */
+ if (cinfo->privateInfo && cinfo->privateInfo->digcx)
+ NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
+
+ /* at this point, we have the plain decoded & decrypted data
+ ** which is either more encoded DER (which we need to hand to the child
+ ** decoder) or data we need to hand back to our caller
+ */
+
+ /* pass the content back to our caller or */
+ /* feed our freshly decrypted and decoded data into child decoder */
+ if (p7dcx->cb != NULL) {
+ (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
+ }
+#if 1
+ else
+#endif
+ if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
+ /* store it in "inner" data item as well */
+ /* find the DATA item in the encapsulated cinfo and store it there */
+ NSSCMSDecoderData *decoderData =
+ (NSSCMSDecoderData *)cinfo->content.pointer;
+ SECItem *dataItem = &decoderData->data;
+
+ offset = dataItem->len;
+ if (dataItem->len + len > decoderData->totalBufferSize) {
+ int needLen = (dataItem->len + len) * 2;
+ dest = (unsigned char *)
+ PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen);
+ if (dest == NULL) {
+ p7dcx->error = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ if (dataItem->len) {
+ PORT_Memcpy(dest, dataItem->data, dataItem->len);
+ }
+ decoderData->totalBufferSize = needLen;
+ dataItem->data = dest;
+ }
+
+ /* copy it in */
+ PORT_Memcpy(dataItem->data + offset, data, len);
+ dataItem->len += len;
+ }
+
+done:
+loser:
+ if (buf)
+ PORT_Free(buf);
+}
+
+/*
+ * nss_cms_decoder_update_filter - process ASN.1 data
+ *
+ * once we have set up a filter in nss_cms_decoder_notify(),
+ * all data processed by the ASN.1 decoder is also passed through here.
+ * we pass the content bytes (as opposed to length and tag bytes) on to
+ * nss_cms_decoder_work_data().
+ */
+static void
+nss_cms_decoder_update_filter(void *arg, const char *data, unsigned long len,
+ int depth, SEC_ASN1EncodingPart data_kind)
+{
+ NSSCMSDecoderContext *p7dcx;
+
+ PORT_Assert(len); /* paranoia */
+ if (len == 0)
+ return;
+
+ p7dcx = (NSSCMSDecoderContext *)arg;
+
+ p7dcx->saw_contents = PR_TRUE;
+
+ /* pass on the content bytes only */
+ if (data_kind == SEC_ASN1_Contents)
+ nss_cms_decoder_work_data(p7dcx, (const unsigned char *)data, len,
+ PR_FALSE);
+}
+
+/*
+ * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
+ *
+ * "poolp" - pointer to arena for message, or NULL if new pool should be created
+ * "cb", "cb_arg" - callback function and argument for delivery of inner content
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ */
+NSSCMSDecoderContext *
+NSS_CMSDecoder_Start(PLArenaPool *poolp,
+ NSSCMSContentCallback cb, void *cb_arg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg)
+{
+ NSSCMSDecoderContext *p7dcx;
+ NSSCMSMessage *cmsg;
+
+ cmsg = NSS_CMSMessage_Create(poolp);
+ if (cmsg == NULL)
+ return NULL;
+
+ NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb,
+ decrypt_key_cb_arg, NULL, NULL);
+
+ p7dcx = PORT_ZNew(NSSCMSDecoderContext);
+ if (p7dcx == NULL) {
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+ }
+
+ p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate);
+ if (p7dcx->dcx == NULL) {
+ PORT_Free(p7dcx);
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+ }
+
+ SEC_ASN1DecoderSetNotifyProc(p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
+
+ p7dcx->cmsg = cmsg;
+ p7dcx->type = SEC_OID_UNKNOWN;
+
+ p7dcx->cb = cb;
+ p7dcx->cb_arg = cb_arg;
+ p7dcx->first_decoded = PR_FALSE;
+ p7dcx->need_indefinite_finish = PR_FALSE;
+ return p7dcx;
+}
+
+/*
+ * NSS_CMSDecoder_Update - feed DER-encoded data to decoder
+ */
+SECStatus
+NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf,
+ unsigned long len)
+{
+ SECStatus rv = SECSuccess;
+ if (p7dcx->dcx != NULL && p7dcx->error == 0) {
+ /* if error is set already, don't bother */
+ if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) && (p7dcx->first_decoded == PR_TRUE) && (buf[0] == SEC_ASN1_INTEGER)) {
+ /* Microsoft Windows 2008 left out the Sequence wrapping in some
+ * of their kerberos replies. If we are here, we most likely are
+ * dealing with one of those replies. Supply the Sequence wrap
+ * as indefinite encoding (since we don't know the total length
+ * yet) */
+ static const char lbuf[2] = { SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, 0x80 };
+ rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* ok, we're going to need the indefinite finish when we are done */
+ p7dcx->need_indefinite_finish = PR_TRUE;
+ }
+
+ rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len);
+ }
+
+loser:
+ p7dcx->first_decoded = PR_FALSE;
+ if (rv != SECSuccess) {
+ p7dcx->error = PORT_GetError();
+ PORT_Assert(p7dcx->error);
+ if (p7dcx->error == 0)
+ p7dcx->error = -1;
+ }
+
+ if (p7dcx->error == 0)
+ return SECSuccess;
+
+ /* there has been a problem, let's finish the decoder */
+ if (p7dcx->dcx != NULL) {
+ (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
+ p7dcx->dcx = NULL;
+ }
+ PORT_SetError(p7dcx->error);
+
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSDecoder_Cancel - stop decoding in case of error
+ */
+void
+NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx)
+{
+ if (p7dcx->dcx != NULL)
+ (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
+ NSS_CMSMessage_Destroy(p7dcx->cmsg);
+ PORT_Free(p7dcx);
+}
+
+/*
+ * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
+ */
+NSSCMSMessage *
+NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx)
+{
+ NSSCMSMessage *cmsg;
+
+ cmsg = p7dcx->cmsg;
+
+ if (p7dcx->dcx == NULL ||
+ SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
+ nss_cms_after_end(p7dcx) != SECSuccess) {
+ NSS_CMSMessage_Destroy(cmsg); /* get rid of pool if it's ours */
+ cmsg = NULL;
+ }
+
+ PORT_Free(p7dcx);
+ return cmsg;
+}
+
+NSSCMSMessage *
+NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
+ NSSCMSContentCallback cb, void *cb_arg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg)
+{
+ NSSCMSDecoderContext *p7dcx;
+
+ /* first arg(poolp) == NULL => create our own pool */
+ p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg,
+ decrypt_key_cb, decrypt_key_cb_arg);
+ if (p7dcx == NULL)
+ return NULL;
+ NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len);
+ return NSS_CMSDecoder_Finish(p7dcx);
+}
diff --git a/security/nss/lib/smime/cmsdigdata.c b/security/nss/lib/smime/cmsdigdata.c
new file mode 100644
index 0000000000..a249686bbe
--- /dev/null
+++ b/security/nss/lib/smime/cmsdigdata.c
@@ -0,0 +1,212 @@
+/* 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 digestedData methods.
+ */
+
+#include "cmslocal.h"
+
+#include "secitem.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+
+/*
+ * NSS_CMSDigestedData_Create - create a digestedData object (presumably for encoding)
+ *
+ * version will be set by NSS_CMSDigestedData_Encode_BeforeStart
+ * digestAlg is passed as parameter
+ * contentInfo must be filled by the user
+ * digest will be calculated while encoding
+ */
+NSSCMSDigestedData *
+NSS_CMSDigestedData_Create(NSSCMSMessage *cmsg, SECAlgorithmID *digestalg)
+{
+ void *mark;
+ NSSCMSDigestedData *digd;
+ PLArenaPool *poolp;
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ digd = (NSSCMSDigestedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSDigestedData));
+ if (digd == NULL)
+ goto loser;
+
+ digd->cmsg = cmsg;
+
+ if (SECOID_CopyAlgorithmID(poolp, &(digd->digestAlg), digestalg) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return digd;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/*
+ * NSS_CMSDigestedData_Destroy - destroy a digestedData object
+ */
+void
+NSS_CMSDigestedData_Destroy(NSSCMSDigestedData *digd)
+{
+ /* everything's in a pool, so don't worry about the storage */
+ if (digd != NULL) {
+ NSS_CMSContentInfo_Destroy(&(digd->contentInfo));
+ }
+ return;
+}
+
+/*
+ * NSS_CMSDigestedData_GetContentInfo - return pointer to digestedData object's contentInfo
+ */
+NSSCMSContentInfo *
+NSS_CMSDigestedData_GetContentInfo(NSSCMSDigestedData *digd)
+{
+ return &(digd->contentInfo);
+}
+
+/*
+ * NSS_CMSDigestedData_Encode_BeforeStart - do all the necessary things to a DigestedData
+ * before encoding begins.
+ *
+ * In particular:
+ * - set the right version number. The contentInfo's content type must be set up already.
+ */
+SECStatus
+NSS_CMSDigestedData_Encode_BeforeStart(NSSCMSDigestedData *digd)
+{
+ unsigned long version;
+ SECItem *dummy;
+
+ version = NSS_CMS_DIGESTED_DATA_VERSION_DATA;
+ if (!NSS_CMSType_IsData(NSS_CMSContentInfo_GetContentTypeTag(
+ &(digd->contentInfo))))
+ version = NSS_CMS_DIGESTED_DATA_VERSION_ENCAP;
+
+ dummy = SEC_ASN1EncodeInteger(digd->cmsg->poolp, &(digd->version), version);
+ return (dummy == NULL) ? SECFailure : SECSuccess;
+}
+
+/*
+ * NSS_CMSDigestedData_Encode_BeforeData - do all the necessary things to a DigestedData
+ * before the encapsulated data is passed through the encoder.
+ *
+ * In detail:
+ * - set up the digests if necessary
+ */
+SECStatus
+NSS_CMSDigestedData_Encode_BeforeData(NSSCMSDigestedData *digd)
+{
+ SECStatus rv = NSS_CMSContentInfo_Private_Init(&digd->contentInfo);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* set up the digests */
+ if (digd->digestAlg.algorithm.len != 0 && digd->digest.len == 0) {
+ /* if digest is already there, do nothing */
+ digd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartSingle(&(digd->digestAlg));
+ if (digd->contentInfo.privateInfo->digcx == NULL)
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSDigestedData_Encode_AfterData - do all the necessary things to a DigestedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - finish the digests
+ */
+SECStatus
+NSS_CMSDigestedData_Encode_AfterData(NSSCMSDigestedData *digd)
+{
+ SECStatus rv = SECSuccess;
+ /* did we have digest calculation going on? */
+ if (digd->contentInfo.privateInfo && digd->contentInfo.privateInfo->digcx) {
+ rv = NSS_CMSDigestContext_FinishSingle(digd->contentInfo.privateInfo->digcx,
+ digd->cmsg->poolp,
+ &(digd->digest));
+ /* error has been set by NSS_CMSDigestContext_FinishSingle */
+ digd->contentInfo.privateInfo->digcx = NULL;
+ }
+
+ return rv;
+}
+
+/*
+ * NSS_CMSDigestedData_Decode_BeforeData - do all the necessary things to a DigestedData
+ * before the encapsulated data is passed through the encoder.
+ *
+ * In detail:
+ * - set up the digests if necessary
+ */
+SECStatus
+NSS_CMSDigestedData_Decode_BeforeData(NSSCMSDigestedData *digd)
+{
+ SECStatus rv;
+
+ /* is there a digest algorithm yet? */
+ if (digd->digestAlg.algorithm.len == 0)
+ return SECFailure;
+
+ rv = NSS_CMSContentInfo_Private_Init(&digd->contentInfo);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ digd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartSingle(&(digd->digestAlg));
+ if (digd->contentInfo.privateInfo->digcx == NULL)
+ return SECFailure;
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSDigestedData_Decode_AfterData - do all the necessary things to a DigestedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - finish the digests
+ */
+SECStatus
+NSS_CMSDigestedData_Decode_AfterData(NSSCMSDigestedData *digd)
+{
+ SECStatus rv = SECSuccess;
+ /* did we have digest calculation going on? */
+ if (digd->contentInfo.privateInfo && digd->contentInfo.privateInfo->digcx) {
+ rv = NSS_CMSDigestContext_FinishSingle(digd->contentInfo.privateInfo->digcx,
+ digd->cmsg->poolp,
+ &(digd->cdigest));
+ /* error has been set by NSS_CMSDigestContext_FinishSingle */
+ digd->contentInfo.privateInfo->digcx = NULL;
+ }
+
+ return rv;
+}
+
+/*
+ * NSS_CMSDigestedData_Decode_AfterEnd - finalize a digestedData.
+ *
+ * In detail:
+ * - check the digests for equality
+ */
+SECStatus
+NSS_CMSDigestedData_Decode_AfterEnd(NSSCMSDigestedData *digd)
+{
+ /* did we have digest calculation going on? */
+ if (digd->cdigest.len != 0) {
+ /* XXX comparision btw digest & cdigest */
+ /* XXX set status */
+ /* TODO!!!! */
+ }
+
+ return SECSuccess;
+}
diff --git a/security/nss/lib/smime/cmsdigest.c b/security/nss/lib/smime/cmsdigest.c
new file mode 100644
index 0000000000..1eb88f0b63
--- /dev/null
+++ b/security/nss/lib/smime/cmsdigest.c
@@ -0,0 +1,259 @@
+/* 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 digesting.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+/* #define CMS_FIND_LEAK_MULTIPLE 1 */
+#ifdef CMS_FIND_LEAK_MULTIPLE
+static int stop_on_err = 1;
+static int global_num_digests = 0;
+#endif
+
+struct digestPairStr {
+ const SECHashObject *digobj;
+ void *digcx;
+};
+typedef struct digestPairStr digestPair;
+
+struct NSSCMSDigestContextStr {
+ PRBool saw_contents;
+ PLArenaPool *pool;
+ int digcnt;
+ digestPair *digPairs;
+};
+
+/*
+ * NSS_CMSDigestContext_StartMultiple - start digest calculation using all the
+ * digest algorithms in "digestalgs" in parallel.
+ */
+NSSCMSDigestContext *
+NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs)
+{
+ PLArenaPool *pool;
+ NSSCMSDigestContext *cmsdigcx;
+ int digcnt;
+ int i;
+
+#ifdef CMS_FIND_LEAK_MULTIPLE
+ PORT_Assert(global_num_digests == 0 || !stop_on_err);
+#endif
+
+ digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs);
+ /* It's OK if digcnt is zero. We have to allow this for "certs only"
+ ** messages.
+ */
+ pool = PORT_NewArena(2048);
+ if (!pool)
+ return NULL;
+
+ cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext);
+ if (cmsdigcx == NULL)
+ goto loser;
+
+ cmsdigcx->saw_contents = PR_FALSE;
+ cmsdigcx->pool = pool;
+ cmsdigcx->digcnt = digcnt;
+
+ cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt);
+ if (cmsdigcx->digPairs == NULL) {
+ goto loser;
+ }
+
+ /*
+ * Create a digest object context for each algorithm.
+ */
+ for (i = 0; i < digcnt; i++) {
+ const SECHashObject *digobj;
+ void *digcx;
+
+ digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]);
+ /*
+ * Skip any algorithm we do not even recognize; obviously,
+ * this could be a problem, but if it is critical then the
+ * result will just be that the signature does not verify.
+ * We do not necessarily want to error out here, because
+ * the particular algorithm may not actually be important,
+ * but we cannot know that until later.
+ */
+ if (digobj == NULL)
+ continue;
+
+ digcx = (*digobj->create)();
+ if (digcx != NULL) {
+ (*digobj->begin)(digcx);
+ cmsdigcx->digPairs[i].digobj = digobj;
+ cmsdigcx->digPairs[i].digcx = digcx;
+#ifdef CMS_FIND_LEAK_MULTIPLE
+ global_num_digests++;
+#endif
+ }
+ }
+ return cmsdigcx;
+
+loser:
+ /* no digest objects have been created, or need to be destroyed. */
+ if (pool) {
+ PORT_FreeArena(pool, PR_FALSE);
+ }
+ return NULL;
+}
+
+/*
+ * NSS_CMSDigestContext_StartSingle - same as
+ * NSS_CMSDigestContext_StartMultiple, but only one algorithm.
+ */
+NSSCMSDigestContext *
+NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg)
+{
+ SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
+
+ digestalgs[0] = digestalg;
+ return NSS_CMSDigestContext_StartMultiple(digestalgs);
+}
+
+/*
+ * NSS_CMSDigestContext_Update - feed more data into the digest machine
+ */
+void
+NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx,
+ const unsigned char *data, int len)
+{
+ int i;
+ digestPair *pair = cmsdigcx->digPairs;
+
+ cmsdigcx->saw_contents = PR_TRUE;
+
+ for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
+ if (pair->digcx) {
+ (*pair->digobj->update)(pair->digcx, data, len);
+ }
+ }
+}
+
+/*
+ * NSS_CMSDigestContext_Cancel - cancel digesting operation
+ */
+void
+NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
+{
+ int i;
+ digestPair *pair = cmsdigcx->digPairs;
+
+ for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
+ if (pair->digcx) {
+ (*pair->digobj->destroy)(pair->digcx, PR_TRUE);
+#ifdef CMS_FIND_LEAK_MULTIPLE
+ --global_num_digests;
+#endif
+ }
+ }
+#ifdef CMS_FIND_LEAK_MULTIPLE
+ PORT_Assert(global_num_digests == 0 || !stop_on_err);
+#endif
+ PORT_FreeArena(cmsdigcx->pool, PR_FALSE);
+}
+
+/*
+ * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
+ * into an array of SECItems (allocated on poolp)
+ */
+SECStatus
+NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx,
+ PLArenaPool *poolp,
+ SECItem ***digestsp)
+{
+ SECItem **digests = NULL;
+ digestPair *pair;
+ void *mark;
+ int i;
+ SECStatus rv;
+
+ /* no contents? do not finish digests */
+ if (digestsp == NULL || !cmsdigcx->saw_contents) {
+ rv = SECSuccess;
+ goto cleanup;
+ }
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* allocate digest array & SECItems on arena */
+ digests = PORT_ArenaNewArray(poolp, SECItem *, cmsdigcx->digcnt + 1);
+
+ rv = ((digests == NULL) ? SECFailure : SECSuccess);
+ pair = cmsdigcx->digPairs;
+ for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) {
+ SECItem digest;
+ unsigned char hash[HASH_LENGTH_MAX];
+
+ if (!pair->digcx) {
+ digests[i] = NULL;
+ continue;
+ }
+
+ digest.type = siBuffer;
+ digest.data = hash;
+ digest.len = pair->digobj->length;
+ (*pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len);
+ digests[i] = SECITEM_ArenaDupItem(poolp, &digest);
+ if (!digests[i]) {
+ rv = SECFailure;
+ }
+ }
+ digests[i] = NULL;
+ if (rv == SECSuccess) {
+ PORT_ArenaUnmark(poolp, mark);
+ } else
+ PORT_ArenaRelease(poolp, mark);
+
+cleanup:
+ NSS_CMSDigestContext_Cancel(cmsdigcx);
+ /* Don't change the caller's digests pointer if we have no digests.
+ ** NSS_CMSSignedData_Encode_AfterData depends on this behavior.
+ */
+ if (rv == SECSuccess && digestsp && digests) {
+ *digestsp = digests;
+ }
+ return rv;
+}
+
+/*
+ * NSS_CMSDigestContext_FinishSingle - same as
+ * NSS_CMSDigestContext_FinishMultiple, but for one digest.
+ */
+SECStatus
+NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx,
+ PLArenaPool *poolp,
+ SECItem *digest)
+{
+ SECStatus rv = SECFailure;
+ SECItem **dp = NULL;
+ PLArenaPool *arena = NULL;
+
+ if ((arena = PORT_NewArena(1024)) == NULL)
+ goto loser;
+
+ /* get the digests into arena, then copy the first digest into poolp */
+ rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp);
+ if (rv == SECSuccess && dp) {
+ /* now copy it into poolp */
+ rv = SECITEM_CopyItem(poolp, digest, dp[0]);
+ }
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return rv;
+}
diff --git a/security/nss/lib/smime/cmsencdata.c b/security/nss/lib/smime/cmsencdata.c
new file mode 100644
index 0000000000..f2a27468ec
--- /dev/null
+++ b/security/nss/lib/smime/cmsencdata.c
@@ -0,0 +1,262 @@
+/* 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 encryptedData methods.
+ */
+
+#include "cmslocal.h"
+
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+#include "secpkcs5.h"
+
+/*
+ * NSS_CMSEncryptedData_Create - create an empty encryptedData object.
+ *
+ * "algorithm" specifies the bulk encryption algorithm to use.
+ * "keysize" is the key size.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+NSSCMSEncryptedData *
+NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm,
+ int keysize)
+{
+ void *mark;
+ NSSCMSEncryptedData *encd;
+ PLArenaPool *poolp;
+ SECAlgorithmID *pbe_algid;
+ SECStatus rv;
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ encd = PORT_ArenaZNew(poolp, NSSCMSEncryptedData);
+ if (encd == NULL)
+ goto loser;
+
+ encd->cmsg = cmsg;
+
+ /* version is set in NSS_CMSEncryptedData_Encode_BeforeStart() */
+
+ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
+ rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(encd->contentInfo),
+ algorithm, NULL, keysize);
+ } 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 NSS_CMS_EncryptedData_Create
+ * to create pkcs5v2 PBEs */
+ pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL);
+ if (pbe_algid == NULL) {
+ rv = SECFailure;
+ } else {
+ rv = NSS_CMSContentInfo_SetContentEncAlgID(poolp,
+ &(encd->contentInfo),
+ pbe_algid, keysize);
+ SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
+ }
+ }
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return encd;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/*
+ * NSS_CMSEncryptedData_Destroy - destroy an encryptedData object
+ */
+void
+NSS_CMSEncryptedData_Destroy(NSSCMSEncryptedData *encd)
+{
+ /* everything's in a pool, so don't worry about the storage */
+ if (encd != NULL) {
+ NSS_CMSContentInfo_Destroy(&(encd->contentInfo));
+ }
+ return;
+}
+
+/*
+ * NSS_CMSEncryptedData_GetContentInfo - return pointer to encryptedData object's contentInfo
+ */
+NSSCMSContentInfo *
+NSS_CMSEncryptedData_GetContentInfo(NSSCMSEncryptedData *encd)
+{
+ return &(encd->contentInfo);
+}
+
+/*
+ * NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData
+ * before encoding begins.
+ *
+ * In particular:
+ * - set the correct version value.
+ * - get the encryption key
+ */
+SECStatus
+NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd)
+{
+ int version;
+ PK11SymKey *bulkkey = NULL;
+ SECItem *dummy;
+ NSSCMSContentInfo *cinfo = &(encd->contentInfo);
+
+ if (NSS_CMSArray_IsEmpty((void **)encd->unprotectedAttr))
+ version = NSS_CMS_ENCRYPTED_DATA_VERSION;
+ else
+ version = NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR;
+
+ dummy = SEC_ASN1EncodeInteger(encd->cmsg->poolp, &(encd->version), version);
+ if (dummy == NULL)
+ return SECFailure;
+
+ /* now get content encryption key (bulk key) by using our cmsg callback */
+ if (encd->cmsg->decrypt_key_cb)
+ bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg,
+ NSS_CMSContentInfo_GetContentEncAlg(cinfo));
+ if (bulkkey == NULL)
+ return SECFailure;
+
+ /* store the bulk key in the contentInfo so that the encoder can find it */
+ NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
+ PK11_FreeSymKey(bulkkey);
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEncryptedData_Encode_BeforeData - set up encryption
+ */
+SECStatus
+NSS_CMSEncryptedData_Encode_BeforeData(NSSCMSEncryptedData *encd)
+{
+ NSSCMSContentInfo *cinfo;
+ PK11SymKey *bulkkey;
+ SECAlgorithmID *algid;
+ SECStatus rv;
+
+ cinfo = &(encd->contentInfo);
+
+ /* find bulkkey and algorithm - must have been set by NSS_CMSEncryptedData_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 therefore essential that algid is a pointer to the "real" contentEncAlg,
+ * not just to a copy */
+ cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(encd->cmsg->poolp,
+ bulkkey, algid);
+ PK11_FreeSymKey(bulkkey);
+ if (cinfo->privateInfo->ciphcx == NULL)
+ return SECFailure;
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEncryptedData_Encode_AfterData - finalize this encryptedData for encoding
+ */
+SECStatus
+NSS_CMSEncryptedData_Encode_AfterData(NSSCMSEncryptedData *encd)
+{
+ if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) {
+ NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx);
+ encd->contentInfo.privateInfo->ciphcx = NULL;
+ }
+
+ /* nothing to do after data */
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEncryptedData_Decode_BeforeData - find bulk key & set up decryption
+ */
+SECStatus
+NSS_CMSEncryptedData_Decode_BeforeData(NSSCMSEncryptedData *encd)
+{
+ PK11SymKey *bulkkey = NULL;
+ NSSCMSContentInfo *cinfo;
+ SECAlgorithmID *bulkalg;
+ SECStatus rv = SECFailure;
+
+ cinfo = &(encd->contentInfo);
+
+ bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
+
+ if (encd->cmsg->decrypt_key_cb == NULL) /* no callback? no key../ */
+ goto loser;
+
+ bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg);
+ if (bulkkey == NULL)
+ /* no success finding a bulk key */
+ goto loser;
+
+ NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
+
+ 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 */
+
+ /* we are done with (this) bulkkey now. */
+ PK11_FreeSymKey(bulkkey);
+
+ rv = SECSuccess;
+
+loser:
+ return rv;
+}
+
+/*
+ * NSS_CMSEncryptedData_Decode_AfterData - finish decrypting this encryptedData's content
+ */
+SECStatus
+NSS_CMSEncryptedData_Decode_AfterData(NSSCMSEncryptedData *encd)
+{
+ if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) {
+ NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx);
+ encd->contentInfo.privateInfo->ciphcx = NULL;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSEncryptedData_Decode_AfterEnd - finish decoding this encryptedData
+ */
+SECStatus
+NSS_CMSEncryptedData_Decode_AfterEnd(NSSCMSEncryptedData *encd)
+{
+ /* apply final touches */
+ return SECSuccess;
+}
diff --git a/security/nss/lib/smime/cmsencode.c b/security/nss/lib/smime/cmsencode.c
new file mode 100644
index 0000000000..703492b5e3
--- /dev/null
+++ b/security/nss/lib/smime/cmsencode.c
@@ -0,0 +1,762 @@
+/* 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 encoding.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+struct nss_cms_encoder_output {
+ NSSCMSContentCallback outputfn;
+ void *outputarg;
+ PLArenaPool *destpoolp;
+ SECItem *dest;
+};
+
+struct NSSCMSEncoderContextStr {
+ SEC_ASN1EncoderContext *ecx; /* ASN.1 encoder context */
+ PRBool ecxupdated; /* true if data was handed in */
+ NSSCMSMessage *cmsg; /* pointer to the root message */
+ SECOidTag type; /* type tag of the current content */
+ NSSCMSContent content; /* pointer to current content */
+ struct nss_cms_encoder_output output; /* output function */
+ int error; /* error code */
+ NSSCMSEncoderContext *childp7ecx; /* link to child encoder context */
+};
+
+static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
+static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
+static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx,
+ const char *data, unsigned long len);
+static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
+ const unsigned char *data, unsigned long len,
+ PRBool final, PRBool innermost);
+
+extern const SEC_ASN1Template NSSCMSMessageTemplate[];
+
+/*
+ * The little output function that the ASN.1 encoder calls to hand
+ * us bytes which we in turn hand back to our caller (via the callback
+ * they gave us).
+ */
+static void
+nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
+ int depth, SEC_ASN1EncodingPart data_kind)
+{
+ struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
+ unsigned char *dest;
+ unsigned long offset;
+
+#ifdef CMSDEBUG
+ int i;
+ const char *data_name = "unknown";
+
+ switch (data_kind) {
+ case SEC_ASN1_Identifier:
+ data_name = "identifier";
+ break;
+ case SEC_ASN1_Length:
+ data_name = "length";
+ break;
+ case SEC_ASN1_Contents:
+ data_name = "contents";
+ break;
+ case SEC_ASN1_EndOfContents:
+ data_name = "end-of-contents";
+ break;
+ }
+ fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len);
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
+ }
+ if ((i % 16) != 0)
+ fprintf(stderr, "\n");
+#endif
+
+ if (output->outputfn != NULL)
+ /* call output callback with DER data */
+ output->outputfn(output->outputarg, buf, len);
+
+ if (output->dest != NULL) {
+ /* store DER data in SECItem */
+ offset = output->dest->len;
+ if (offset == 0) {
+ dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
+ } else {
+ dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
+ output->dest->data,
+ output->dest->len,
+ output->dest->len + len);
+ }
+ if (dest == NULL)
+ /* oops */
+ return;
+
+ output->dest->data = dest;
+ output->dest->len += len;
+
+ /* copy it in */
+ if (len) {
+ PORT_Memcpy(output->dest->data + offset, buf, len);
+ }
+ }
+}
+
+/*
+ * nss_cms_encoder_notify - ASN.1 encoder callback
+ *
+ * this function is called by the ASN.1 encoder before and after the encoding of
+ * every object. here, it is used to keep track of data structures, set up
+ * encryption and/or digesting and possibly set up child encoders.
+ */
+static void
+nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
+{
+ NSSCMSEncoderContext *p7ecx;
+ NSSCMSContentInfo *rootcinfo, *cinfo;
+ PRBool after = !before;
+ SECOidTag childtype;
+ SECItem *item;
+
+ p7ecx = (NSSCMSEncoderContext *)arg;
+ PORT_Assert(p7ecx != NULL);
+
+ rootcinfo = &(p7ecx->cmsg->contentInfo);
+
+#ifdef CMSDEBUG
+ fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after",
+ dest, depth);
+#endif
+
+ /*
+ * Watch for the content field, at which point we want to instruct
+ * the ASN.1 encoder to start taking bytes from the buffer.
+ */
+ if (NSS_CMSType_IsData(p7ecx->type)) {
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ if (before && dest == &(cinfo->rawContent)) {
+ /* just set up encoder to grab from user - no encryption or digesting */
+ if ((item = cinfo->content.data) != NULL)
+ (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data,
+ item->len, PR_TRUE, PR_TRUE);
+ else
+ SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
+ SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
+ }
+ } else if (NSS_CMSType_IsWrapper(p7ecx->type)) {
+ /* when we know what the content is, we encode happily until we reach the inner content */
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ if (after && dest == &(cinfo->contentType)) {
+ /* we're right before encoding the data (if we have some or not) */
+ /* (for encrypted data, we're right before the contentEncAlg which may change */
+ /* in nss_cms_before_data because of IV calculation when setting up encryption) */
+ if (nss_cms_before_data(p7ecx) != SECSuccess)
+ p7ecx->error = PORT_GetError();
+ }
+ if (before && dest == &(cinfo->rawContent)) {
+ if (p7ecx->childp7ecx == NULL) {
+ if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) {
+ /* we are the innermost non-data and we have data - feed it in */
+ (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data,
+ item->len, PR_TRUE, PR_TRUE);
+ } else {
+ /* else we'll have to get data from user */
+ SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
+ }
+ } else {
+ /* if we have a nested encoder, wait for its data */
+ SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
+ }
+ }
+ if (after && dest == &(cinfo->rawContent)) {
+ if (nss_cms_after_data(p7ecx) != SECSuccess)
+ p7ecx->error = PORT_GetError();
+ SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
+ }
+ } else {
+ /* we're still in the root message */
+ if (after && dest == &(rootcinfo->contentType)) {
+ /* got the content type OID now - so find out the type tag */
+ p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
+ /* set up a pointer to our current content */
+ p7ecx->content = rootcinfo->content;
+ }
+ }
+}
+
+/*
+ * nss_cms_before_data - setup the current encoder to receive data
+ */
+static SECStatus
+nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
+{
+ SECStatus rv;
+ SECOidTag childtype;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSEncoderContext *childp7ecx;
+ const SEC_ASN1Template *template;
+
+ /* call _Encode_BeforeData handlers */
+ switch (p7ecx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ /* we're encoding a signedData, so set up the digests */
+ rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ /* we're encoding a digestedData, so set up the digest */
+ rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
+ break;
+ default:
+ if (NSS_CMSType_IsWrapper(p7ecx->type)) {
+ rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type,
+ p7ecx->content.genericData);
+ } else {
+ rv = SECFailure;
+ }
+ }
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ /* ok, now we have a pointer to cinfo */
+ /* find out what kind of data is encapsulated */
+
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ if (NSS_CMSType_IsWrapper(childtype)) {
+ /* in these cases, we need to set up a child encoder! */
+ /* create new encoder context */
+ childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
+ if (childp7ecx == NULL)
+ return SECFailure;
+
+ /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
+ * (which will encrypt and/or digest it)
+ * this needs to route back into our update function
+ * which finds the lowest encoding context & encrypts and computes digests */
+ childp7ecx->type = childtype;
+ childp7ecx->content = cinfo->content;
+ /* use the non-recursive update function here, of course */
+ childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
+ childp7ecx->output.outputarg = p7ecx;
+ childp7ecx->output.destpoolp = NULL;
+ childp7ecx->output.dest = NULL;
+ childp7ecx->cmsg = p7ecx->cmsg;
+ childp7ecx->ecxupdated = PR_FALSE;
+ childp7ecx->childp7ecx = NULL;
+
+ template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
+ if (template == NULL)
+ goto loser; /* cannot happen */
+
+ /* now initialize the data for encoding the first third */
+ switch (childp7ecx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
+ break;
+ default:
+ rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type,
+ cinfo->content.genericData);
+ break;
+ }
+ if (rv != SECSuccess)
+ goto loser;
+
+ /*
+ * Initialize the BER encoder.
+ */
+ childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
+ nss_cms_encoder_out, &(childp7ecx->output));
+ if (childp7ecx->ecx == NULL)
+ goto loser;
+
+ /*
+ * Indicate that we are streaming. We will be streaming until we
+ * get past the contents bytes.
+ */
+ if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
+ SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
+
+ /*
+ * The notify function will watch for the contents field.
+ */
+ p7ecx->childp7ecx = childp7ecx;
+ SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify,
+ childp7ecx);
+
+ /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
+ /* encoding process - we'll do that from the update function instead */
+ /* otherwise we'd be encoding data from a call of the notify function of the */
+ /* parent encoder (which would not work) */
+
+ } else if (NSS_CMSType_IsData(childtype)) {
+ p7ecx->childp7ecx = NULL;
+ } else {
+ /* we do not know this type */
+ p7ecx->error = SEC_ERROR_BAD_DER;
+ }
+
+ return SECSuccess;
+
+loser:
+ if (childp7ecx) {
+ if (childp7ecx->ecx)
+ SEC_ASN1EncoderFinish(childp7ecx->ecx);
+ PORT_Free(childp7ecx);
+ p7ecx->childp7ecx = NULL;
+ }
+ return SECFailure;
+}
+
+static SECStatus
+nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
+{
+ SECStatus rv = SECFailure;
+
+ switch (p7ecx->type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ /* this will finish the digests and sign */
+ rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
+ break;
+ default:
+ if (NSS_CMSType_IsWrapper(p7ecx->type)) {
+ rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type,
+ p7ecx->content.genericData);
+ } else {
+ rv = SECFailure;
+ }
+ break;
+ }
+ return rv;
+}
+
+/*
+ * nss_cms_encoder_work_data - process incoming data
+ *
+ * (from the user or the next encoding layer)
+ * Here, we need to digest and/or encrypt, then pass it on
+ */
+static SECStatus
+nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
+ const unsigned char *data, unsigned long len,
+ PRBool final, PRBool innermost)
+{
+ unsigned char *buf = NULL;
+ SECStatus rv;
+ NSSCMSContentInfo *cinfo;
+
+ rv = SECSuccess; /* may as well be optimistic */
+
+ /*
+ * We should really have data to process, or we should be trying
+ * to finish/flush the last block. (This is an overly paranoid
+ * check since all callers are in this file and simple inspection
+ * proves they do it right. But it could find a bug in future
+ * modifications/development, that is why it is here.)
+ */
+ PORT_Assert((data != NULL && len) || final);
+
+ /* we got data (either from the caller, or from a lower level encoder) */
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ if (!cinfo) {
+ /* The original programmer didn't expect this to happen */
+ p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
+ return SECFailure;
+ }
+
+ /* Update the running digest. */
+ if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL)
+ NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
+
+ /* Encrypt this chunk. */
+ if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
+ unsigned int inlen; /* length of data being encrypted */
+ unsigned int outlen; /* length of encrypted data */
+ unsigned int buflen; /* length available for encrypted data */
+
+ inlen = len;
+ buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx,
+ inlen, final);
+ if (buflen == 0) {
+ /*
+ * No output is expected, but the input data may be buffered
+ * so we still have to call Encrypt.
+ */
+ rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0,
+ data, inlen, final);
+ if (final) {
+ len = 0;
+ goto done;
+ }
+ return rv;
+ }
+
+ if (dest != NULL)
+ buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
+ else
+ buf = (unsigned char *)PORT_Alloc(buflen);
+
+ if (buf == NULL) {
+ rv = SECFailure;
+ } else {
+ rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf,
+ &outlen, buflen,
+ data, inlen, final);
+ data = buf;
+ len = outlen;
+ }
+ if (rv != SECSuccess)
+ /* encryption or malloc failed? */
+ return rv;
+ }
+
+ /*
+ * at this point (data,len) has everything we'd like to give to the CURRENT encoder
+ * (which will encode it, then hand it back to the user or the parent encoder)
+ * We don't encode the data if we're innermost and we're told not to include the data
+ */
+ if (p7ecx->ecx != NULL && len &&
+ (!innermost || cinfo->rawContent != cinfo->content.pointer))
+ rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
+
+done:
+
+ if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
+ if (dest != NULL) {
+ dest->data = buf;
+ dest->len = len;
+ } else if (buf != NULL) {
+ PORT_Free(buf);
+ }
+ }
+ return rv;
+}
+
+/*
+ * nss_cms_encoder_update - deliver encoded data to the next higher level
+ *
+ * no recursion here because we REALLY want to end up at the next higher encoder!
+ */
+static SECStatus
+nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
+{
+ /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
+ return nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data,
+ len, PR_FALSE, PR_FALSE);
+}
+
+/*
+ * NSS_CMSEncoder_Start - set up encoding of a CMS message
+ *
+ * "cmsg" - message to encode
+ * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
+ * will not be called if NULL.
+ * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
+ * "destpoolp" - pool to allocate DER-encoded output in
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ * "detached_digestalgs", "detached_digests" - digests from detached content
+ */
+NSSCMSEncoderContext *
+NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
+ NSSCMSContentCallback outputfn, void *outputarg,
+ SECItem *dest, PLArenaPool *destpoolp,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
+ SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
+{
+ NSSCMSEncoderContext *p7ecx;
+ SECStatus rv;
+ NSSCMSContentInfo *cinfo;
+ SECOidTag tag;
+
+ NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
+ detached_digestalgs, detached_digests);
+
+ p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
+ if (p7ecx == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ p7ecx->cmsg = cmsg;
+ p7ecx->output.outputfn = outputfn;
+ p7ecx->output.outputarg = outputarg;
+ p7ecx->output.dest = dest;
+ p7ecx->output.destpoolp = destpoolp;
+ p7ecx->type = SEC_OID_UNKNOWN;
+
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+
+ tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ switch (tag) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
+ break;
+ default:
+ if (NSS_CMSType_IsWrapper(tag)) {
+ rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag,
+ p7ecx->content.genericData);
+ } else {
+ rv = SECFailure;
+ }
+ break;
+ }
+ if (rv != SECSuccess) {
+ PORT_Free(p7ecx);
+ return NULL;
+ }
+
+ /* Initialize the BER encoder.
+ * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
+ p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
+ nss_cms_encoder_out, &(p7ecx->output));
+ if (p7ecx->ecx == NULL) {
+ PORT_Free(p7ecx);
+ return NULL;
+ }
+ p7ecx->ecxupdated = PR_FALSE;
+
+ /*
+ * Indicate that we are streaming. We will be streaming until we
+ * get past the contents bytes.
+ */
+ if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
+ SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
+
+ /*
+ * The notify function will watch for the contents field.
+ */
+ SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
+
+ /* this will kick off the encoding process & encode everything up to the content bytes,
+ * at which point the notify function sets streaming mode (and possibly creates
+ * a child encoder). */
+ p7ecx->ecxupdated = PR_TRUE;
+ if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
+ PORT_Free(p7ecx);
+ return NULL;
+ }
+
+ return p7ecx;
+}
+
+/*
+ * NSS_CMSEncoder_Update - take content data delivery from the user
+ *
+ * "p7ecx" - encoder context
+ * "data" - content data
+ * "len" - length of content data
+ *
+ * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
+ * then hand the data to the work_data fn
+ */
+SECStatus
+NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
+{
+ SECStatus rv;
+ NSSCMSContentInfo *cinfo;
+ SECOidTag childtype;
+
+ if (p7ecx->error)
+ return SECFailure;
+
+ /* hand data to the innermost decoder */
+ if (p7ecx->childp7ecx) {
+ /* tell the child to start encoding, up to its first data byte, if it
+ * hasn't started yet */
+ if (!p7ecx->childp7ecx->ecxupdated) {
+ p7ecx->childp7ecx->ecxupdated = PR_TRUE;
+ if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess)
+ return SECFailure;
+ }
+ /* recursion here */
+ rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
+ } else {
+ /* we are at innermost decoder */
+ /* find out about our inner content type - must be data */
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ if (!cinfo) {
+ /* The original programmer didn't expect this to happen */
+ p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
+ return SECFailure;
+ }
+
+ childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ if (!NSS_CMSType_IsData(childtype))
+ return SECFailure;
+ /* and we must not have preset data */
+ if (cinfo->content.data != NULL)
+ return SECFailure;
+
+ /* hand it the data so it can encode it (let DER trickle up the chain) */
+ rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data,
+ len, PR_FALSE, PR_TRUE);
+ }
+ return rv;
+}
+
+/*
+ * NSS_CMSEncoder_Cancel - stop all encoding
+ *
+ * we need to walk down the chain of encoders and the finish them from the innermost out
+ */
+SECStatus
+NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
+{
+ SECStatus rv = SECFailure;
+
+ /* XXX do this right! */
+
+ /*
+ * Finish any inner decoders before us so that all the encoded data is flushed
+ * This basically finishes all the decoders from the innermost to the outermost.
+ * Finishing an inner decoder may result in data being updated to the outer decoder
+ * while we are already in NSS_CMSEncoder_Finish, but that's allright.
+ */
+ if (p7ecx->childp7ecx) {
+ rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
+ /* remember rv for now */
+#ifdef CMSDEBUG
+ if (rv != SECSuccess) {
+ fprintf(stderr, "Fail to cancel inner encoder\n");
+ }
+#endif
+ }
+
+ /*
+ * On the way back up, there will be no more data (if we had an
+ * inner encoder, it is done now!)
+ * Flush out any remaining data and/or finish digests.
+ */
+ rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
+ if (rv != SECSuccess)
+ goto loser;
+
+ p7ecx->childp7ecx = NULL;
+
+ /* kick the encoder back into working mode again.
+ * We turn off streaming stuff (which will cause the encoder to continue
+ * encoding happily, now that we have all the data (like digests) ready for it).
+ */
+ SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
+ SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
+
+ /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
+ rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
+
+loser:
+ SEC_ASN1EncoderFinish(p7ecx->ecx);
+ PORT_Free(p7ecx);
+ return rv;
+}
+
+/*
+ * NSS_CMSEncoder_Finish - signal the end of data
+ *
+ * we need to walk down the chain of encoders and the finish them from the innermost out
+ */
+SECStatus
+NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
+{
+ SECStatus rv = SECFailure;
+ NSSCMSContentInfo *cinfo;
+
+ /*
+ * Finish any inner decoders before us so that all the encoded data is flushed
+ * This basically finishes all the decoders from the innermost to the outermost.
+ * Finishing an inner decoder may result in data being updated to the outer decoder
+ * while we are already in NSS_CMSEncoder_Finish, but that's allright.
+ */
+ if (p7ecx->childp7ecx) {
+ /* tell the child to start encoding, up to its first data byte, if it
+ * hasn't yet */
+ if (!p7ecx->childp7ecx->ecxupdated) {
+ p7ecx->childp7ecx->ecxupdated = PR_TRUE;
+ rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0);
+ if (rv != SECSuccess) {
+ NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
+ goto loser;
+ }
+ }
+ rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ /*
+ * On the way back up, there will be no more data (if we had an
+ * inner encoder, it is done now!)
+ * Flush out any remaining data and/or finish digests.
+ */
+ rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
+ if (rv != SECSuccess)
+ goto loser;
+
+ p7ecx->childp7ecx = NULL;
+
+ cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
+ if (!cinfo) {
+ /* The original programmer didn't expect this to happen */
+ p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
+ rv = SECFailure;
+ goto loser;
+ }
+ SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
+ SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
+ /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
+ rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
+
+ if (p7ecx->error)
+ rv = SECFailure;
+
+loser:
+ SEC_ASN1EncoderFinish(p7ecx->ecx);
+ PORT_Free(p7ecx);
+ return rv;
+}
diff --git a/security/nss/lib/smime/cmsenvdata.c b/security/nss/lib/smime/cmsenvdata.c
new file mode 100644
index 0000000000..95b3fb98be
--- /dev/null
+++ b/security/nss/lib/smime/cmsenvdata.c
@@ -0,0 +1,403 @@
+/* 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 "keyhi.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);
+
+ if (cinfo == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ goto loser;
+ }
+
+ 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;
+}
diff --git a/security/nss/lib/smime/cmslocal.h b/security/nss/lib/smime/cmslocal.h
new file mode 100644
index 0000000000..ef7d500280
--- /dev/null
+++ b/security/nss/lib/smime/cmslocal.h
@@ -0,0 +1,351 @@
+/* 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/. */
+
+/*
+ * Support routines for CMS implementation, none of which are exported.
+ *
+ * Do not export this file! If something in here is really needed outside
+ * of smime code, first try to add a CMS interface which will do it for
+ * you. If that has a problem, then just move out what you need, changing
+ * its name as appropriate!
+ */
+
+#ifndef _CMSLOCAL_H_
+#define _CMSLOCAL_H_
+
+#include "cms.h"
+#include "cmsreclist.h"
+#include "secasn1t.h"
+
+extern const SEC_ASN1Template NSSCMSContentInfoTemplate[];
+
+struct NSSCMSContentInfoPrivateStr {
+ NSSCMSCipherContext *ciphcx;
+ NSSCMSDigestContext *digcx;
+ PRBool dontStream;
+};
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * private content Info stuff
+ */
+
+/* initialize the private content info field. If this returns
+ * SECSuccess, the cinfo->private field is safe to dereference.
+ */
+SECStatus NSS_CMSContentInfo_Private_Init(NSSCMSContentInfo *cinfo);
+
+/***********************************************************************
+ * cmscipher.c - en/decryption routines
+ ***********************************************************************/
+
+/*
+ * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
+ * based on the given bulk * encryption key and algorithm identifier (which may include an iv).
+ */
+extern NSSCMSCipherContext *
+NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid);
+
+/*
+ * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
+ * based on the given bulk encryption key and algorithm tag. Fill in the algorithm
+ * identifier (which may include an iv) appropriately.
+ */
+extern NSSCMSCipherContext *
+NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid);
+
+extern void
+NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc);
+
+/*
+ * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
+ *
+ * cc - the cipher context
+ * input_len - number of bytes used as input
+ * final - true if this is the final chunk of data
+ *
+ * Result can be used to perform memory allocations. Note that the amount
+ * is exactly accurate only when not doing a block cipher or when final
+ * is false, otherwise it is an upper bound on the amount because until
+ * we see the data we do not know how many padding bytes there are
+ * (always between 1 and bsize).
+ */
+extern unsigned int
+NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final);
+
+/*
+ * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
+ *
+ * cc - the cipher context
+ * input_len - number of bytes used as input
+ * final - true if this is the final chunk of data
+ *
+ * Result can be used to perform memory allocations.
+ */
+extern unsigned int
+NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final);
+
+/*
+ * NSS_CMSCipherContext_Decrypt - do the decryption
+ *
+ * cc - the cipher context
+ * output - buffer for decrypted result bytes
+ * output_len_p - number of bytes in output
+ * max_output_len - upper bound on bytes to put into output
+ * input - pointer to input bytes
+ * input_len - number of input bytes
+ * final - true if this is the final chunk of data
+ *
+ * Decrypts a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the decrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "cc" is the return value from NSS_CMSCipher_StartDecrypt.
+ * When "final" is true, this is the last of the data to be decrypted.
+ */
+extern SECStatus
+NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final);
+
+/*
+ * NSS_CMSCipherContext_Encrypt - do the encryption
+ *
+ * cc - the cipher context
+ * output - buffer for decrypted result bytes
+ * output_len_p - number of bytes in output
+ * max_output_len - upper bound on bytes to put into output
+ * input - pointer to input bytes
+ * input_len - number of input bytes
+ * final - true if this is the final chunk of data
+ *
+ * Encrypts a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the encrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "cc" is the return value from NSS_CMSCipher_StartEncrypt.
+ * When "final" is true, this is the last of the data to be encrypted.
+ */
+extern SECStatus
+NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final);
+
+/************************************************************************
+ * cmspubkey.c - public key operations
+ ************************************************************************/
+
+/*
+ * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
+ *
+ * this function takes a symmetric key and encrypts it using an RSA public key
+ * according to PKCS#1 and RFC2633 (S/MIME)
+ */
+extern SECStatus
+NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert,
+ PK11SymKey *key,
+ SECItem *encKey);
+
+extern SECStatus
+NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
+ SECKEYPublicKey *publickey,
+ PK11SymKey *bulkkey, SECItem *encKey);
+
+/*
+ * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
+ *
+ * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
+ * key handle. Please note that the actual unwrapped key data may not be allowed to leave
+ * a hardware token...
+ */
+extern PK11SymKey *
+NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag);
+
+extern SECStatus
+NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
+ SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
+ SECItem *originatorPubKey);
+
+extern PK11SymKey *
+NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey,
+ SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg);
+
+/************************************************************************
+ * cmsreclist.c - recipient list stuff
+ ************************************************************************/
+extern NSSCMSRecipient **nss_cms_recipient_list_create(NSSCMSRecipientInfo **recipientinfos);
+extern void nss_cms_recipient_list_destroy(NSSCMSRecipient **recipient_list);
+extern NSSCMSRecipientEncryptedKey *NSS_CMSRecipientEncryptedKey_Create(PLArenaPool *poolp);
+
+/************************************************************************
+ * cmsarray.c - misc array functions
+ ************************************************************************/
+/*
+ * NSS_CMSArray_Alloc - allocate an array in an arena
+ */
+extern void **
+NSS_CMSArray_Alloc(PLArenaPool *poolp, int n);
+
+/*
+ * NSS_CMSArray_Add - add an element to the end of an array
+ */
+extern SECStatus
+NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj);
+
+/*
+ * NSS_CMSArray_IsEmpty - check if array is empty
+ */
+extern PRBool
+NSS_CMSArray_IsEmpty(void **array);
+
+/*
+ * NSS_CMSArray_Count - count number of elements in array
+ */
+extern int
+NSS_CMSArray_Count(void **array);
+
+/*
+ * NSS_CMSArray_Sort - sort an array ascending, in place
+ *
+ * If "secondary" is not NULL, the same reordering gets applied to it.
+ * If "tertiary" is not NULL, the same reordering gets applied to it.
+ * "compare" is a function that returns
+ * < 0 when the first element is less than the second
+ * = 0 when the first element is equal to the second
+ * > 0 when the first element is greater than the second
+ */
+extern void
+NSS_CMSArray_Sort(void **primary, int (*compare)(void *, void *), void **secondary, void **tertiary);
+
+/************************************************************************
+ * cmsattr.c - misc attribute functions
+ ************************************************************************/
+/*
+ * NSS_CMSAttribute_Create - create an attribute
+ *
+ * if value is NULL, the attribute won't have a value. It can be added later
+ * with NSS_CMSAttribute_AddValue.
+ */
+extern NSSCMSAttribute *
+NSS_CMSAttribute_Create(PLArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded);
+
+/*
+ * NSS_CMSAttribute_AddValue - add another value to an attribute
+ */
+extern SECStatus
+NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value);
+
+/*
+ * NSS_CMSAttribute_GetType - return the OID tag
+ */
+extern SECOidTag
+NSS_CMSAttribute_GetType(NSSCMSAttribute *attr);
+
+/*
+ * NSS_CMSAttribute_GetValue - return the first attribute value
+ *
+ * We do some sanity checking first:
+ * - Multiple values are *not* expected.
+ * - Empty values are *not* expected.
+ */
+extern SECItem *
+NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr);
+
+/*
+ * NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
+ */
+extern PRBool
+NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av);
+
+/*
+ * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
+ *
+ * If you are wondering why this routine does not reorder the attributes
+ * first, and might be tempted to make it do so, see the comment by the
+ * call to ReorderAttributes in cmsencode.c. (Or, see who else calls this
+ * and think long and hard about the implications of making it always
+ * do the reordering.)
+ */
+extern SECItem *
+NSS_CMSAttributeArray_Encode(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest);
+
+/*
+ * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
+ *
+ * make sure that the order of the attributes guarantees valid DER (which must be
+ * in lexigraphically ascending order for a SET OF); if reordering is necessary it
+ * will be done in place (in attrs).
+ */
+extern SECStatus
+NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs);
+
+/*
+ * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
+ * find one that matches the specified object ID.
+ *
+ * If "only" is true, then make sure that there is not more than one attribute
+ * of the same type. Otherwise, just return the first one found. (XXX Does
+ * anybody really want that first-found behavior? It was like that when I found it...)
+ */
+extern NSSCMSAttribute *
+NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only);
+
+/*
+ * NSS_CMSAttributeArray_AddAttr - add an attribute to an
+ * array of attributes.
+ */
+extern SECStatus
+NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr);
+
+/*
+ * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
+ */
+extern SECStatus
+NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs,
+ SECOidTag type, SECItem *value, PRBool encoded);
+
+/*
+ * NSS_CMSSignedData_AddTempCertificate - add temporary certificate references.
+ * They may be needed for signature verification on the data, for example.
+ */
+extern SECStatus
+NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert);
+
+/*
+ * local function to handle compatibility issues
+ * by mapping a signature algorithm back to a digest.
+ */
+SECOidTag NSS_CMSUtil_MapSignAlgs(SECOidTag signAlg);
+
+/************************************************************************/
+
+/*
+ * local functions to handle user defined S/MIME content types
+ */
+
+PRBool NSS_CMSType_IsWrapper(SECOidTag type);
+PRBool NSS_CMSType_IsData(SECOidTag type);
+size_t NSS_CMSType_GetContentSize(SECOidTag type);
+const SEC_ASN1Template *NSS_CMSType_GetTemplate(SECOidTag type);
+
+void NSS_CMSGenericWrapperData_Destroy(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+SECStatus NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd);
+
+SEC_END_PROTOS
+
+#endif /* _CMSLOCAL_H_ */
diff --git a/security/nss/lib/smime/cmsmessage.c b/security/nss/lib/smime/cmsmessage.c
new file mode 100644
index 0000000000..366b71abad
--- /dev/null
+++ b/security/nss/lib/smime/cmsmessage.c
@@ -0,0 +1,345 @@
+/* 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 message methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+/*
+ * NSS_CMSMessage_Create - create a CMS message object
+ *
+ * "poolp" - arena to allocate memory from, or NULL if new arena should be created
+ */
+NSSCMSMessage *
+NSS_CMSMessage_Create(PLArenaPool *poolp)
+{
+ void *mark = NULL;
+ NSSCMSMessage *cmsg;
+ PRBool poolp_is_ours = PR_FALSE;
+
+ if (poolp == NULL) {
+ poolp = PORT_NewArena(1024); /* XXX what is right value? */
+ if (poolp == NULL) {
+ return NULL;
+ }
+ poolp_is_ours = PR_TRUE;
+ }
+
+ if (!poolp_is_ours)
+ mark = PORT_ArenaMark(poolp);
+
+ cmsg = (NSSCMSMessage *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSMessage));
+ if (cmsg == NULL ||
+ NSS_CMSContentInfo_Private_Init(&(cmsg->contentInfo)) != SECSuccess) {
+ if (!poolp_is_ours) {
+ if (mark) {
+ PORT_ArenaRelease(poolp, mark);
+ }
+ } else {
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
+ return NULL;
+ }
+
+ cmsg->poolp = poolp;
+ cmsg->poolp_is_ours = poolp_is_ours;
+ cmsg->refCount = 1;
+
+ if (mark) {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return cmsg;
+}
+
+/*
+ * NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding
+ *
+ * "cmsg" - message object
+ * "pwfn", pwfn_arg" - callback function for getting token password
+ * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
+ * "detached_digestalgs", "detached_digests" - digests from detached content
+ */
+void
+NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg,
+ PK11PasswordFunc pwfn, void *pwfn_arg,
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
+ SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
+{
+ if (cmsg == NULL) {
+ return;
+ }
+ if (pwfn) {
+ PK11_SetPasswordFunc(pwfn);
+ }
+
+ cmsg->pwfn_arg = pwfn_arg;
+ cmsg->decrypt_key_cb = decrypt_key_cb;
+ cmsg->decrypt_key_cb_arg = decrypt_key_cb_arg;
+ cmsg->detached_digestalgs = detached_digestalgs;
+ cmsg->detached_digests = detached_digests;
+}
+
+/*
+ * NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces.
+ */
+void
+NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg)
+{
+ if (cmsg == NULL)
+ return;
+
+ PORT_Assert(cmsg->refCount > 0);
+ if (cmsg->refCount <= 0) { /* oops */
+ return;
+ }
+
+ cmsg->refCount--; /* thread safety? */
+ if (cmsg->refCount > 0) {
+ return;
+ }
+
+ NSS_CMSContentInfo_Destroy(&(cmsg->contentInfo));
+
+ /* if poolp is not NULL, cmsg is the owner of its arena */
+ if (cmsg->poolp_is_ours) {
+ PORT_FreeArena(cmsg->poolp, PR_FALSE); /* XXX clear it? */
+ }
+}
+
+/*
+ * NSS_CMSMessage_Copy - return a copy of the given message.
+ *
+ * The copy may be virtual or may be real -- either way, the result needs
+ * to be passed to NSS_CMSMessage_Destroy later (as does the original).
+ */
+NSSCMSMessage *
+NSS_CMSMessage_Copy(NSSCMSMessage *cmsg)
+{
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ PORT_Assert(cmsg->refCount > 0);
+
+ cmsg->refCount++; /* XXX chrisk thread safety? */
+ return cmsg;
+}
+
+/*
+ * NSS_CMSMessage_GetArena - return a pointer to the message's arena pool
+ */
+PLArenaPool *
+NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg)
+{
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ return cmsg->poolp;
+}
+
+/*
+ * NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo
+ */
+NSSCMSContentInfo *
+NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg)
+{
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ return &(cmsg->contentInfo);
+}
+
+/*
+ * Return a pointer to the actual content.
+ * In the case of those types which are encrypted, this returns the *plain* content.
+ * In case of nested contentInfos, this descends and retrieves the innermost content.
+ */
+SECItem *
+NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg)
+{
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ /* this is a shortcut */
+ NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ SECItem *pItem = NSS_CMSContentInfo_GetInnerContent(cinfo);
+ return pItem;
+}
+
+/*
+ * NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message
+ *
+ * CMS data content objects do not count.
+ */
+int
+NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg)
+{
+ int count = 0;
+ NSSCMSContentInfo *cinfo;
+
+ if (cmsg == NULL) {
+ return 0;
+ }
+
+ /* walk down the chain of contentinfos */
+ for (cinfo = &(cmsg->contentInfo); cinfo != NULL;) {
+ count++;
+ cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo);
+ }
+ return count;
+}
+
+/*
+ * NSS_CMSMessage_ContentLevel - find content level #n
+ *
+ * CMS data content objects do not count.
+ */
+NSSCMSContentInfo *
+NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n)
+{
+ int count = 0;
+ NSSCMSContentInfo *cinfo;
+
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ /* walk down the chain of contentinfos */
+ for (cinfo = &(cmsg->contentInfo); cinfo != NULL && count < n;
+ cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
+ count++;
+ }
+
+ return cinfo;
+}
+
+/*
+ * NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way
+ */
+PRBool
+NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg)
+{
+ NSSCMSContentInfo *cinfo;
+
+ if (cmsg == NULL) {
+ return PR_FALSE;
+ }
+
+ /* descend into CMS message */
+ for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
+ cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
+ if (!NSS_CMSType_IsData(NSS_CMSContentInfo_GetContentTypeTag(cinfo)))
+ continue; /* next level */
+
+ if (NSS_CMSSignedData_ContainsCertsOrCrls(cinfo->content.signedData))
+ return PR_TRUE;
+ /* callback here for generic wrappers? */
+ }
+ return PR_FALSE;
+}
+
+/*
+ * NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage
+ */
+PRBool
+NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg)
+{
+ NSSCMSContentInfo *cinfo;
+
+ if (cmsg == NULL) {
+ return PR_FALSE;
+ }
+
+ /* walk down the chain of contentinfos */
+ for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
+ cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
+ switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ return PR_TRUE;
+ default:
+ /* callback here for generic wrappers? */
+ break;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+ * NSS_CMSMessage_IsSigned - see if message contains a signed submessage
+ *
+ * If the CMS message has a SignedData with a signature (not just a SignedData)
+ * return true; false otherwise. This can/should be called before calling
+ * VerifySignature, which will always indicate failure if no signature is
+ * present, but that does not mean there even was a signature!
+ * Note that the content itself can be empty (detached content was sent
+ * another way); it is the presence of the signature that matters.
+ */
+PRBool
+NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg)
+{
+ NSSCMSContentInfo *cinfo;
+
+ if (cmsg == NULL) {
+ return PR_FALSE;
+ }
+
+ /* walk down the chain of contentinfos */
+ for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
+ cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
+ switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (cinfo->content.signedData == NULL) {
+ return PR_FALSE;
+ }
+ if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos)) {
+ return PR_TRUE;
+ }
+ break;
+ default:
+ /* callback here for generic wrappers? */
+ break;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+ * NSS_CMSMessage_IsContentEmpty - see if content is empty
+ *
+ * returns PR_TRUE is innermost content length is < minLen
+ * XXX need the encrypted content length (why?)
+ */
+PRBool
+NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen)
+{
+ SECItem *item = NULL;
+
+ if (cmsg == NULL) {
+ return PR_TRUE;
+ }
+
+ item = NSS_CMSContentInfo_GetContent(NSS_CMSMessage_GetContentInfo(cmsg));
+
+ if (!item) {
+ return PR_TRUE;
+ } else if (item->len <= minLen) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
diff --git a/security/nss/lib/smime/cmspubkey.c b/security/nss/lib/smime/cmspubkey.c
new file mode 100644
index 0000000000..8f18f60de1
--- /dev/null
+++ b/security/nss/lib/smime/cmspubkey.c
@@ -0,0 +1,285 @@
+/* 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 public key crypto
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+/* ====== RSA ======================================================================= */
+
+/*
+ * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
+ *
+ * this function takes a symmetric key and encrypts it using an RSA public key
+ * according to PKCS#1 and RFC2633 (S/MIME)
+ */
+SECStatus
+NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert,
+ PK11SymKey *bulkkey,
+ SECItem *encKey)
+{
+ SECStatus rv;
+ SECKEYPublicKey *publickey;
+
+ publickey = CERT_ExtractPublicKey(cert);
+ if (publickey == NULL)
+ return SECFailure;
+
+ rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
+ SECKEY_DestroyPublicKey(publickey);
+ return rv;
+}
+
+SECStatus
+NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
+ SECKEYPublicKey *publickey,
+ PK11SymKey *bulkkey, SECItem *encKey)
+{
+ SECStatus rv;
+ int data_len;
+ KeyType keyType;
+ void *mark = NULL;
+
+ mark = PORT_ArenaMark(poolp);
+ if (!mark)
+ goto loser;
+
+ /* sanity check */
+ keyType = SECKEY_GetPublicKeyType(publickey);
+ PORT_Assert(keyType == rsaKey);
+ if (keyType != rsaKey) {
+ goto loser;
+ }
+ /* allocate memory for the encrypted key */
+ data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
+ encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, data_len);
+ encKey->len = data_len;
+ if (encKey->data == NULL)
+ goto loser;
+
+ /* encrypt the key now */
+ rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
+ publickey, bulkkey, encKey);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ if (mark) {
+ PORT_ArenaRelease(poolp, mark);
+ }
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
+ *
+ * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
+ * key handle. Please note that the actual unwrapped key data may not be allowed to leave
+ * a hardware token...
+ */
+PK11SymKey *
+NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
+{
+ /* that's easy */
+ CK_MECHANISM_TYPE target;
+ PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
+ target = PK11_AlgtagToMechanism(bulkalgtag);
+ if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+ return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
+}
+
+/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
+
+SECStatus
+NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
+ SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
+ SECItem *pubKey)
+{
+#if 0 /* not yet done */
+ SECOidTag certalgtag; /* the certificate's encryption algorithm */
+ SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
+ SECStatus rv;
+ SECItem *params = NULL;
+ int data_len;
+ SECStatus err;
+ PK11SymKey *tek;
+ CERTCertificate *ourCert;
+ SECKEYPublicKey *ourPubKey;
+ NSSCMSKEATemplateSelector whichKEA = NSSCMSKEAInvalid;
+
+ certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
+ PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
+
+ /* We really want to show our KEA tag as the key exchange algorithm tag. */
+ encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
+
+ /* Get the public key of the recipient. */
+ publickey = CERT_ExtractPublicKey(cert);
+ if (publickey == NULL) goto loser;
+
+ /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
+ /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
+ if (ourCert == NULL) goto loser;
+
+ arena = PORT_NewArena(1024);
+ if (arena == NULL) goto loser;
+
+ /* While we're here, extract the key pair's public key data and copy it into */
+ /* the outgoing parameters. */
+ /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
+ if (ourPubKey == NULL)
+ {
+ goto loser;
+ }
+ SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
+ SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
+ ourPubKey = NULL;
+
+ /* Extract our private key in order to derive the KEA key. */
+ ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
+ CERT_DestroyCertificate(ourCert); /* we're done with this */
+ if (!ourPrivKey) goto loser;
+
+ /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
+ if (ukm) {
+ ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
+ ukm->len = /* XXXX */;
+ }
+
+ /* Generate the KEK (key exchange key) according to RFC2631 which we use
+ * to wrap the bulk encryption key. */
+ kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
+ ukm, NULL,
+ /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
+ CKA_WRAP, 0, wincx);
+
+ SECKEY_DestroyPublicKey(publickey);
+ SECKEY_DestroyPrivateKey(ourPrivKey);
+ publickey = NULL;
+ ourPrivKey = NULL;
+
+ if (!kek)
+ goto loser;
+
+ /* allocate space for the encrypted CEK (bulk key) */
+ encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
+ encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
+
+ if (encKey->data == NULL)
+ {
+ PK11_FreeSymKey(kek);
+ goto loser;
+ }
+
+
+ /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
+ /* bulk encryption algorithm */
+ switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
+ {
+ case /* XXXX */CKM_SKIPJACK_CFB8:
+ err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
+ whichKEA = NSSCMSKEAUsesSkipjack;
+ break;
+ case /* XXXX */CKM_SKIPJACK_CFB8:
+ err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
+ whichKEA = NSSCMSKEAUsesSkipjack;
+ break;
+ default:
+ /* XXXX what do we do here? Neither RC2 nor 3DES... */
+ err = SECFailure;
+ /* set error */
+ break;
+ }
+
+ PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
+ if (err != SECSuccess)
+ goto loser;
+
+ PORT_Assert(whichKEA != NSSCMSKEAInvalid);
+
+ /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
+ /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
+ params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
+ if (params == NULL)
+ goto loser;
+
+ /* now set keyEncAlg */
+ rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* XXXXXXX this is not right yet */
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if (publickey) {
+ SECKEY_DestroyPublicKey(publickey);
+ }
+ if (ourPrivKey) {
+ SECKEY_DestroyPrivateKey(ourPrivKey);
+ }
+#endif
+ return SECFailure;
+}
+
+PK11SymKey *
+NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey,
+ SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag,
+ void *pwfn_arg)
+{
+#if 0 /* not yet done */
+ SECStatus err;
+ CK_MECHANISM_TYPE bulkType;
+ PK11SymKey *tek;
+ SECKEYPublicKey *originatorPubKey;
+ NSSCMSSMIMEKEAParameters keaParams;
+
+ /* XXXX get originator's public key */
+ originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
+ keaParams.originatorKEAKey.len);
+ if (originatorPubKey == NULL)
+ goto loser;
+
+ /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
+ The Derive function generates a shared secret and combines it with the originatorRA
+ data to come up with an unique session key */
+ tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
+ &keaParams.originatorRA, NULL,
+ CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
+ CKA_WRAP, 0, pwfn_arg);
+ SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
+ if (tek == NULL)
+ goto loser;
+
+ /* Now that we have the TEK, unwrap the bulk key
+ with which to decrypt the message. */
+ /* Skipjack is being used as the bulk encryption algorithm.*/
+ /* Unwrap the bulk key. */
+ bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
+ encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
+
+ return bulkkey;
+
+loser:
+#endif
+ return NULL;
+}
diff --git a/security/nss/lib/smime/cmsrecinfo.c b/security/nss/lib/smime/cmsrecinfo.c
new file mode 100644
index 0000000000..20dd698e8f
--- /dev/null
+++ b/security/nss/lib/smime/cmsrecinfo.c
@@ -0,0 +1,669 @@
+/* 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 recipientInfo methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+PRBool
+nss_cmsrecipientinfo_usessubjectkeyid(NSSCMSRecipientInfo *ri)
+{
+ if (ri->recipientInfoType == NSSCMSRecipientInfoID_KeyTrans) {
+ NSSCMSRecipientIdentifier *rid;
+ rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
+ if (rid->identifierType == NSSCMSRecipientID_SubjectKeyID) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+ * NOTE: fakeContent marks CMSMessage structure which is only used as a carrier
+ * of pwfn_arg and arena pools. In an ideal world, NSSCMSMessage would not have
+ * been exported, and we would have added an ordinary enum to handle this
+ * check. Unfortunatly wo don't have that luxury so we are overloading the
+ * contentTypeTag field. NO code should every try to interpret this content tag
+ * as a real OID tag, or use any fields other than pwfn_arg or poolp of this
+ * CMSMessage for that matter */
+static const SECOidData fakeContent;
+NSSCMSRecipientInfo *
+nss_cmsrecipientinfo_create(NSSCMSMessage *cmsg,
+ NSSCMSRecipientIDSelector type,
+ CERTCertificate *cert,
+ SECKEYPublicKey *pubKey,
+ SECItem *subjKeyID,
+ void *pwfn_arg,
+ SECItem *DERinput)
+{
+ NSSCMSRecipientInfo *ri;
+ void *mark;
+ SECOidTag certalgtag;
+ SECStatus rv = SECSuccess;
+ NSSCMSRecipientEncryptedKey *rek;
+ NSSCMSOriginatorIdentifierOrKey *oiok;
+ unsigned long version;
+ SECItem *dummy;
+ PLArenaPool *poolp;
+ CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL;
+ NSSCMSRecipientIdentifier *rid;
+ extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
+
+ if (!cmsg) {
+ /* a CMSMessage wasn't supplied, create a fake one to hold the pwfunc
+ * and a private arena pool */
+ cmsg = NSS_CMSMessage_Create(NULL);
+ cmsg->pwfn_arg = pwfn_arg;
+ /* mark it as a special cms message */
+ cmsg->contentInfo.contentTypeTag = (SECOidData *)&fakeContent;
+ }
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ ri = (NSSCMSRecipientInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientInfo));
+ if (ri == NULL)
+ goto loser;
+
+ ri->cmsg = cmsg;
+
+ if (DERinput) {
+ /* decode everything from DER */
+ SECItem newinput;
+ rv = SECITEM_CopyItem(poolp, &newinput, DERinput);
+ if (SECSuccess != rv)
+ goto loser;
+ rv = SEC_QuickDERDecodeItem(poolp, ri, NSSCMSRecipientInfoTemplate, &newinput);
+ if (SECSuccess != rv)
+ goto loser;
+ }
+
+ switch (type) {
+ case NSSCMSRecipientID_IssuerSN: {
+ ri->cert = CERT_DupCertificate(cert);
+ if (NULL == ri->cert)
+ goto loser;
+ spki = &(cert->subjectPublicKeyInfo);
+ break;
+ }
+
+ case NSSCMSRecipientID_SubjectKeyID: {
+ PORT_Assert(pubKey);
+ spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(pubKey);
+ break;
+ }
+
+ case NSSCMSRecipientID_BrandNew:
+ goto done;
+ break;
+
+ default:
+ /* unkown type */
+ goto loser;
+ break;
+ }
+
+ certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm));
+
+ rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
+ switch (certalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans;
+ rid->identifierType = type;
+ if (type == NSSCMSRecipientID_IssuerSN) {
+ rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
+ if (rid->id.issuerAndSN == NULL) {
+ break;
+ }
+ } else if (type == NSSCMSRecipientID_SubjectKeyID) {
+ NSSCMSKeyTransRecipientInfoEx *riExtra;
+
+ rid->id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
+ if (rid->id.subjectKeyID == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ rv = SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID);
+ if (rv != SECSuccess || rid->id.subjectKeyID->data == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ riExtra = &ri->ri.keyTransRecipientInfoEx;
+ riExtra->version = 0;
+ riExtra->pubKey = SECKEY_CopyPublicKey(pubKey);
+ if (riExtra->pubKey == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ }
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
+ PORT_Assert(type == NSSCMSRecipientID_IssuerSN);
+ if (type != NSSCMSRecipientID_IssuerSN) {
+ rv = SECFailure;
+ break;
+ }
+ /* a key agreement op */
+ ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree;
+
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ /* we do not support the case where multiple recipients
+ * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
+ * in this case, we would need to walk all the recipientInfos, take the
+ * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
+ * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
+
+ /* only epheremal-static Diffie-Hellman is supported for now
+ * this is the only form of key agreement that provides potential anonymity
+ * of the sender, plus we do not have to include certs in the message */
+
+ /* force single recipientEncryptedKey for now */
+ if ((rek = NSS_CMSRecipientEncryptedKey_Create(poolp)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* hardcoded IssuerSN choice for now */
+ rek->recipientIdentifier.identifierType = NSSCMSKeyAgreeRecipientID_IssuerSN;
+ if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
+
+ /* see RFC2630 12.3.1.1 */
+ oiok->identifierType = NSSCMSOriginatorIDOrKey_OriginatorPublicKey;
+
+ rv = NSS_CMSArray_Add(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
+ (void *)rek);
+
+ break;
+ default:
+ /* other algorithms not supported yet */
+ /* NOTE that we do not support any KEK algorithm */
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rv = SECFailure;
+ break;
+ }
+
+ if (rv == SECFailure)
+ goto loser;
+
+ /* set version */
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == NSSCMSRecipientID_IssuerSN)
+ version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN;
+ else
+ version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY;
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version);
+ if (dummy == NULL)
+ goto loser;
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version),
+ NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION);
+ if (dummy == NULL)
+ goto loser;
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ /* NOTE: this cannot happen as long as we do not support any KEK algorithm */
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
+ NSS_CMS_KEK_RECIPIENT_INFO_VERSION);
+ if (dummy == NULL)
+ goto loser;
+ break;
+ }
+
+done:
+ PORT_ArenaUnmark(poolp, mark);
+ if (freeSpki)
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+ return ri;
+
+loser:
+ if (ri && ri->cert) {
+ CERT_DestroyCertificate(ri->cert);
+ }
+ if (freeSpki) {
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+ }
+ PORT_ArenaRelease(poolp, mark);
+ if (cmsg->contentInfo.contentTypeTag == &fakeContent) {
+ NSS_CMSMessage_Destroy(cmsg);
+ }
+ return NULL;
+}
+
+/*
+ * NSS_CMSRecipientInfo_Create - create a recipientinfo
+ *
+ * we currently do not create KeyAgreement recipientinfos with multiple
+ * recipientEncryptedKeys the certificate is supposed to have been
+ * verified by the caller
+ */
+NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert)
+{
+ return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_IssuerSN, cert,
+ NULL, NULL, NULL, NULL);
+}
+
+NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateNew(void *pwfn_arg)
+{
+ return nss_cmsrecipientinfo_create(NULL, NSSCMSRecipientID_BrandNew, NULL,
+ NULL, NULL, pwfn_arg, NULL);
+}
+
+NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateFromDER(SECItem *input, void *pwfn_arg)
+{
+ return nss_cmsrecipientinfo_create(NULL, NSSCMSRecipientID_BrandNew, NULL,
+ NULL, NULL, pwfn_arg, input);
+}
+
+NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg,
+ SECItem *subjKeyID,
+ SECKEYPublicKey *pubKey)
+{
+ return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_SubjectKeyID,
+ NULL, pubKey, subjKeyID, NULL, NULL);
+}
+
+NSSCMSRecipientInfo *
+NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert(NSSCMSMessage *cmsg,
+ CERTCertificate *cert)
+{
+ SECKEYPublicKey *pubKey = NULL;
+ SECItem subjKeyID = { siBuffer, NULL, 0 };
+ NSSCMSRecipientInfo *retVal = NULL;
+
+ if (!cmsg || !cert) {
+ return NULL;
+ }
+ pubKey = CERT_ExtractPublicKey(cert);
+ if (!pubKey) {
+ goto done;
+ }
+ if (CERT_FindSubjectKeyIDExtension(cert, &subjKeyID) != SECSuccess ||
+ subjKeyID.data == NULL) {
+ goto done;
+ }
+ retVal = NSS_CMSRecipientInfo_CreateWithSubjKeyID(cmsg, &subjKeyID, pubKey);
+done:
+ if (pubKey)
+ SECKEY_DestroyPublicKey(pubKey);
+
+ if (subjKeyID.data)
+ SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+
+ return retVal;
+}
+
+void
+NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri)
+{
+ if (!ri) {
+ return;
+ }
+ /* version was allocated on the pool, so no need to destroy it */
+ /* issuerAndSN was allocated on the pool, so no need to destroy it */
+ if (ri->cert != NULL)
+ CERT_DestroyCertificate(ri->cert);
+
+ if (nss_cmsrecipientinfo_usessubjectkeyid(ri)) {
+ NSSCMSKeyTransRecipientInfoEx *extra;
+ extra = &ri->ri.keyTransRecipientInfoEx;
+ if (extra->pubKey)
+ SECKEY_DestroyPublicKey(extra->pubKey);
+ }
+ if (ri->cmsg && ri->cmsg->contentInfo.contentTypeTag == &fakeContent) {
+ NSS_CMSMessage_Destroy(ri->cmsg);
+ }
+
+ /* we're done. */
+}
+
+int
+NSS_CMSRecipientInfo_GetVersion(NSSCMSRecipientInfo *ri)
+{
+ unsigned long version;
+ SECItem *versionitem = NULL;
+
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ /* ignore subIndex */
+ versionitem = &(ri->ri.keyTransRecipientInfo.version);
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ /* ignore subIndex */
+ versionitem = &(ri->ri.kekRecipientInfo.version);
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ versionitem = &(ri->ri.keyAgreeRecipientInfo.version);
+ break;
+ }
+
+ PORT_Assert(versionitem);
+ if (versionitem == NULL)
+ return 0;
+
+ /* always take apart the SECItem */
+ if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess)
+ return 0;
+ else
+ return (int)version;
+}
+
+SECItem *
+NSS_CMSRecipientInfo_GetEncryptedKey(NSSCMSRecipientInfo *ri, int subIndex)
+{
+ SECItem *enckey = NULL;
+
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ /* ignore subIndex */
+ enckey = &(ri->ri.keyTransRecipientInfo.encKey);
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ /* ignore subIndex */
+ enckey = &(ri->ri.kekRecipientInfo.encKey);
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
+ break;
+ }
+ return enckey;
+}
+
+SECOidTag
+NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri)
+{
+ SECOidTag encalgtag = SEC_OID_UNKNOWN; /* an invalid encryption alg */
+
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
+ break;
+ }
+ return encalgtag;
+}
+
+SECStatus
+NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey,
+ SECOidTag bulkalgtag)
+{
+ CERTCertificate *cert;
+ SECOidTag certalgtag;
+ SECStatus rv = SECSuccess;
+ NSSCMSRecipientEncryptedKey *rek;
+ NSSCMSOriginatorIdentifierOrKey *oiok;
+ CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL;
+ PLArenaPool *poolp;
+ NSSCMSKeyTransRecipientInfoEx *extra = NULL;
+ PRBool usesSubjKeyID;
+
+ poolp = ri->cmsg->poolp;
+ cert = ri->cert;
+ usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri);
+ if (cert) {
+ spki = &cert->subjectPublicKeyInfo;
+ } else if (usesSubjKeyID) {
+ extra = &ri->ri.keyTransRecipientInfoEx;
+ /* sanity check */
+ PORT_Assert(extra->pubKey);
+ if (!extra->pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(extra->pubKey);
+ } else {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* XXX set ri->recipientInfoType to the proper value here */
+ /* or should we look if it's been set already ? */
+
+ certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm);
+ switch (certalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ /* wrap the symkey */
+ if (cert) {
+ rv = NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey,
+ &ri->ri.keyTransRecipientInfo.encKey);
+ if (rv != SECSuccess)
+ break;
+ } else if (usesSubjKeyID) {
+ PORT_Assert(extra != NULL);
+ rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, extra->pubKey,
+ bulkkey, &ri->ri.keyTransRecipientInfo.encKey);
+ if (rv != SECSuccess)
+ break;
+ }
+
+ rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL);
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
+ rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0];
+ if (rek == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
+ PORT_Assert(oiok->identifierType == NSSCMSOriginatorIDOrKey_OriginatorPublicKey);
+
+ /* see RFC2630 12.3.1.1 */
+ if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
+ SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* this will generate a key pair, compute the shared secret, */
+ /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
+ /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
+ rv = NSS_CMSUtil_EncryptSymKey_ESDH(poolp, cert, bulkkey,
+ &rek->encKey,
+ &ri->ri.keyAgreeRecipientInfo.ukm,
+ &ri->ri.keyAgreeRecipientInfo.keyEncAlg,
+ &oiok->id.originatorPublicKey.publicKey);
+
+ break;
+ default:
+ /* other algorithms not supported yet */
+ /* NOTE that we do not support any KEK algorithm */
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rv = SECFailure;
+ }
+ if (freeSpki)
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+
+ return rv;
+}
+
+PK11SymKey *
+NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex,
+ CERTCertificate *cert, SECKEYPrivateKey *privkey, SECOidTag bulkalgtag)
+{
+ PK11SymKey *bulkkey = NULL;
+ SECOidTag encalgtag;
+ SECItem *enckey;
+ int error;
+
+ ri->cert = CERT_DupCertificate(cert);
+ /* mark the recipientInfo so we can find it later */
+
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */
+ switch (encalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ /* RSA encryption algorithm: */
+ /* get the symmetric (bulk) key by unwrapping it using our private key */
+ bulkkey = NSS_CMSUtil_DecryptSymKey_RSA(privkey, enckey, bulkalgtag);
+ break;
+ default:
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ }
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
+ switch (encalgtag) {
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ /* Diffie-Helman key exchange */
+ /* XXX not yet implemented */
+ /* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */
+ /* we support ephemeral-static DH only, so if the recipientinfo */
+ /* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */
+ /* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */
+ /* content encryption key using a Unwrap op */
+ /* the derive operation has to generate the key using the algorithm in RFC2631 */
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ break;
+ default:
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ }
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.kekRecipientInfo.encKey);
+ /* not supported yet */
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ break;
+ }
+ /* XXXX continue here */
+ return bulkkey;
+
+loser:
+ PORT_SetError(error);
+ return NULL;
+}
+
+SECStatus
+NSS_CMSRecipientInfo_GetCertAndKey(NSSCMSRecipientInfo *ri,
+ CERTCertificate **retcert,
+ SECKEYPrivateKey **retkey)
+{
+ CERTCertificate *cert = NULL;
+ NSSCMSRecipient **recipients = NULL;
+ NSSCMSRecipientInfo *recipientInfos[2];
+ SECStatus rv = SECSuccess;
+ SECKEYPrivateKey *key = NULL;
+
+ if (!ri)
+ return SECFailure;
+
+ if (!retcert && !retkey) {
+ /* nothing requested, nothing found, success */
+ return SECSuccess;
+ }
+
+ if (retcert) {
+ *retcert = NULL;
+ }
+ if (retkey) {
+ *retkey = NULL;
+ }
+
+ if (ri->cert) {
+ cert = CERT_DupCertificate(ri->cert);
+ if (!cert) {
+ rv = SECFailure;
+ }
+ }
+ if (SECSuccess == rv && !cert) {
+ /* we don't have the cert, we have to look for it */
+ /* first build an NSS_CMSRecipient */
+ recipientInfos[0] = ri;
+ recipientInfos[1] = NULL;
+
+ recipients = nss_cms_recipient_list_create(recipientInfos);
+ if (recipients) {
+ /* now look for the cert and key */
+ if (0 == PK11_FindCertAndKeyByRecipientListNew(recipients,
+ ri->cmsg->pwfn_arg)) {
+ cert = CERT_DupCertificate(recipients[0]->cert);
+ key = SECKEY_CopyPrivateKey(recipients[0]->privkey);
+ } else {
+ rv = SECFailure;
+ }
+
+ nss_cms_recipient_list_destroy(recipients);
+ } else {
+ rv = SECFailure;
+ }
+ } else if (SECSuccess == rv && cert && retkey) {
+ /* we have the cert, we just need the key now */
+ key = PK11_FindPrivateKeyFromCert(cert->slot, cert, ri->cmsg->pwfn_arg);
+ }
+ if (retcert) {
+ *retcert = cert;
+ } else {
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ }
+ }
+ if (retkey) {
+ *retkey = key;
+ } else {
+ if (key) {
+ SECKEY_DestroyPrivateKey(key);
+ }
+ }
+
+ return rv;
+}
+
+SECStatus
+NSS_CMSRecipientInfo_Encode(PLArenaPool *poolp,
+ const NSSCMSRecipientInfo *src,
+ SECItem *returned)
+{
+ extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
+ SECStatus rv = SECFailure;
+ if (!src || !returned) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ } else if (SEC_ASN1EncodeItem(poolp, returned, src,
+ NSSCMSRecipientInfoTemplate)) {
+ rv = SECSuccess;
+ }
+ return rv;
+}
diff --git a/security/nss/lib/smime/cmsreclist.c b/security/nss/lib/smime/cmsreclist.c
new file mode 100644
index 0000000000..f753474073
--- /dev/null
+++ b/security/nss/lib/smime/cmsreclist.c
@@ -0,0 +1,170 @@
+/* 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 recipient list functions
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+static int
+nss_cms_recipients_traverse(NSSCMSRecipientInfo **recipientinfos,
+ NSSCMSRecipient **recipient_list)
+{
+ int count = 0;
+ int rlindex = 0;
+ int i, j;
+ NSSCMSRecipient *rle;
+ NSSCMSRecipientInfo *ri;
+ NSSCMSRecipientEncryptedKey *rek;
+
+ for (i = 0; recipientinfos[i] != NULL; i++) {
+ ri = recipientinfos[i];
+ switch (ri->recipientInfoType) {
+ case NSSCMSRecipientInfoID_KeyTrans:
+ if (recipient_list) {
+ NSSCMSRecipientIdentifier *recipId =
+ &ri->ri.keyTransRecipientInfo.recipientIdentifier;
+
+ if (recipId->identifierType != NSSCMSRecipientID_IssuerSN &&
+ recipId->identifierType != NSSCMSRecipientID_SubjectKeyID) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return -1;
+ }
+ /* alloc one & fill it out */
+ rle = (NSSCMSRecipient *)PORT_ZAlloc(sizeof(NSSCMSRecipient));
+ if (!rle)
+ return -1;
+
+ rle->riIndex = i;
+ rle->subIndex = -1;
+ switch (recipId->identifierType) {
+ case NSSCMSRecipientID_IssuerSN:
+ rle->kind = RLIssuerSN;
+ rle->id.issuerAndSN = recipId->id.issuerAndSN;
+ break;
+ case NSSCMSRecipientID_SubjectKeyID:
+ rle->kind = RLSubjKeyID;
+ rle->id.subjectKeyID = recipId->id.subjectKeyID;
+ break;
+ default: /* we never get here because of identifierType check
+ we done before. Leaving it to kill compiler warning */
+ break;
+ }
+ recipient_list[rlindex++] = rle;
+ } else {
+ count++;
+ }
+ break;
+ case NSSCMSRecipientInfoID_KeyAgree:
+ if (ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys == NULL)
+ break;
+ for (j = 0; ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[j] != NULL; j++) {
+ if (recipient_list) {
+ rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[j];
+ /* alloc one & fill it out */
+ rle = (NSSCMSRecipient *)PORT_ZAlloc(sizeof(NSSCMSRecipient));
+ if (!rle)
+ return -1;
+
+ rle->riIndex = i;
+ rle->subIndex = j;
+ switch (rek->recipientIdentifier.identifierType) {
+ case NSSCMSKeyAgreeRecipientID_IssuerSN:
+ rle->kind = RLIssuerSN;
+ rle->id.issuerAndSN = rek->recipientIdentifier.id.issuerAndSN;
+ break;
+ case NSSCMSKeyAgreeRecipientID_RKeyID:
+ rle->kind = RLSubjKeyID;
+ rle->id.subjectKeyID =
+ rek->recipientIdentifier.id.recipientKeyIdentifier.subjectKeyIdentifier;
+ break;
+ }
+ recipient_list[rlindex++] = rle;
+ } else {
+ count++;
+ }
+ }
+ break;
+ case NSSCMSRecipientInfoID_KEK:
+ /* KEK is not implemented */
+ break;
+ }
+ }
+ /* if we have a recipient list, we return on success (-1, above, on failure) */
+ /* otherwise, we return the count. */
+ if (recipient_list) {
+ recipient_list[rlindex] = NULL;
+ return 0;
+ } else {
+ return count;
+ }
+}
+
+NSSCMSRecipient **
+nss_cms_recipient_list_create(NSSCMSRecipientInfo **recipientinfos)
+{
+ int count, rv;
+ NSSCMSRecipient **recipient_list;
+
+ /* count the number of recipient identifiers */
+ count = nss_cms_recipients_traverse(recipientinfos, NULL);
+ if (count <= 0) {
+ /* no recipients? */
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+#if 0
+ PORT_SetErrorString("Cannot find recipient data in envelope.");
+#endif
+ return NULL;
+ }
+
+ /* allocate an array of pointers */
+ recipient_list = (NSSCMSRecipient **)
+ PORT_ZAlloc((count + 1) * sizeof(NSSCMSRecipient *));
+ if (recipient_list == NULL)
+ return NULL;
+
+ /* now fill in the recipient_list */
+ rv = nss_cms_recipients_traverse(recipientinfos, recipient_list);
+ if (rv < 0) {
+ nss_cms_recipient_list_destroy(recipient_list);
+ return NULL;
+ }
+ return recipient_list;
+}
+
+void
+nss_cms_recipient_list_destroy(NSSCMSRecipient **recipient_list)
+{
+ int i;
+ NSSCMSRecipient *recipient;
+
+ for (i = 0; recipient_list[i] != NULL; i++) {
+ recipient = recipient_list[i];
+ if (recipient->cert)
+ CERT_DestroyCertificate(recipient->cert);
+ if (recipient->privkey)
+ SECKEY_DestroyPrivateKey(recipient->privkey);
+ if (recipient->slot)
+ PK11_FreeSlot(recipient->slot);
+ PORT_Free(recipient);
+ }
+ PORT_Free(recipient_list);
+}
+
+NSSCMSRecipientEncryptedKey *
+NSS_CMSRecipientEncryptedKey_Create(PLArenaPool *poolp)
+{
+ return (NSSCMSRecipientEncryptedKey *)PORT_ArenaZAlloc(poolp,
+ sizeof(NSSCMSRecipientEncryptedKey));
+}
diff --git a/security/nss/lib/smime/cmsreclist.h b/security/nss/lib/smime/cmsreclist.h
new file mode 100644
index 0000000000..f424aca6ed
--- /dev/null
+++ b/security/nss/lib/smime/cmsreclist.h
@@ -0,0 +1,27 @@
+/* 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/. */
+
+#ifndef _CMSRECLIST_H
+#define _CMSRECLIST_H
+
+struct NSSCMSRecipientStr {
+ int riIndex; /* this recipient's index in recipientInfo array */
+ int subIndex; /* index into recipientEncryptedKeys */
+ /* (only in NSSCMSKeyAgreeRecipientInfoStr) */
+ enum { RLIssuerSN = 0,
+ RLSubjKeyID = 1 } kind; /* for conversion recipientinfos -> recipientlist */
+ union {
+ CERTIssuerAndSN* issuerAndSN;
+ SECItem* subjectKeyID;
+ } id;
+
+ /* result data (filled out for each recipient that's us) */
+ CERTCertificate* cert;
+ SECKEYPrivateKey* privkey;
+ PK11SlotInfo* slot;
+};
+
+typedef struct NSSCMSRecipientStr NSSCMSRecipient;
+
+#endif /* _CMSRECLIST_H */
diff --git a/security/nss/lib/smime/cmssigdata.c b/security/nss/lib/smime/cmssigdata.c
new file mode 100644
index 0000000000..2538095749
--- /dev/null
+++ b/security/nss/lib/smime/cmssigdata.c
@@ -0,0 +1,1141 @@
+/* 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 signedData methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+/*#include "cdbhdl.h"*/
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+NSSCMSSignedData *
+NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
+{
+ void *mark;
+ NSSCMSSignedData *sigd;
+ PLArenaPool *poolp;
+
+ if (!cmsg) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignedData));
+ if (sigd == NULL)
+ goto loser;
+
+ sigd->cmsg = cmsg;
+
+ /* signerInfos, certs, certlists, crls are all empty */
+ /* version is set in NSS_CMSSignedData_Finalize() */
+
+ PORT_ArenaUnmark(poolp, mark);
+ return sigd;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+void
+NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
+{
+ CERTCertificate **certs, **tempCerts, *cert;
+ CERTCertificateList **certlists, *certlist;
+ NSSCMSSignerInfo **signerinfos, *si;
+
+ if (sigd == NULL)
+ return;
+
+ certs = sigd->certs;
+ tempCerts = sigd->tempCerts;
+ certlists = sigd->certLists;
+ signerinfos = sigd->signerInfos;
+
+ if (certs != NULL) {
+ while ((cert = *certs++) != NULL)
+ CERT_DestroyCertificate(cert);
+ }
+
+ if (tempCerts != NULL) {
+ while ((cert = *tempCerts++) != NULL)
+ CERT_DestroyCertificate(cert);
+ }
+
+ if (certlists != NULL) {
+ while ((certlist = *certlists++) != NULL)
+ CERT_DestroyCertificateList(certlist);
+ }
+
+ if (signerinfos != NULL) {
+ while ((si = *signerinfos++) != NULL)
+ NSS_CMSSignerInfo_Destroy(si);
+ }
+
+ /* everything's in a pool, so don't worry about the storage */
+ NSS_CMSContentInfo_Destroy(&(sigd->contentInfo));
+}
+
+/*
+ * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
+ * before start of encoding.
+ *
+ * In detail:
+ * - find out about the right value to put into sigd->version
+ * - come up with a list of digestAlgorithms (which should be the union of the algorithms
+ * in the signerinfos).
+ * If we happen to have a pre-set list of algorithms (and digest values!), we
+ * check if we have all the signerinfos' algorithms. If not, this is an error.
+ */
+SECStatus
+NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
+{
+ NSSCMSSignerInfo *signerinfo;
+ SECOidTag digestalgtag;
+ SECItem *dummy;
+ int version;
+ SECStatus rv;
+ PRBool haveDigests = PR_FALSE;
+ int n, i;
+ PLArenaPool *poolp;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ poolp = sigd->cmsg->poolp;
+
+ /* we assume that we have precomputed digests if there is a list of algorithms, and */
+ /* a chunk of data for each of those algorithms */
+ if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
+ for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
+ if (sigd->digests[i] == NULL)
+ break;
+ }
+ if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
+ haveDigests = PR_TRUE; /* yes: we must have all the digests */
+ }
+
+ version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
+
+ /* RFC2630 5.1 "version is the syntax version number..." */
+ if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
+ version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
+
+ /* prepare all the SignerInfos (there may be none) */
+ for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
+ signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
+
+ /* RFC2630 5.1 "version is the syntax version number..." */
+ if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
+ version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
+
+ /* collect digestAlgorithms from SignerInfos */
+ /* (we need to know which algorithms we have when the content comes in) */
+ /* do not overwrite any existing digestAlgorithms (and digest) */
+ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
+ n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
+ if (n < 0 && haveDigests) {
+ /* oops, there is a digestalg we do not have a digest for */
+ /* but we were supposed to have all the digests already... */
+ goto loser;
+ } else if (n < 0) {
+ /* add the digestAlgorithm & a NULL digest */
+ rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
+ if (rv != SECSuccess)
+ goto loser;
+ } else {
+ /* found it, nothing to do */
+ }
+ }
+
+ dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
+ if (dummy == NULL)
+ return SECFailure;
+
+ /* this is a SET OF, so we need to sort them guys */
+ rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ (void **)sigd->digests);
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
+{
+ SECStatus rv;
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* set up the digests */
+ if (sigd->digests && sigd->digests[0]) {
+ sigd->contentInfo.privateInfo->digcx = NULL; /* don't attempt to make new ones. */
+ } else if (sigd->digestAlgorithms != NULL) {
+ sigd->contentInfo.privateInfo->digcx =
+ NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
+ if (sigd->contentInfo.privateInfo->digcx == NULL)
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
+ * after all the encapsulated data was passed through the encoder.
+ *
+ * In detail:
+ * - create the signatures in all the SignerInfos
+ *
+ * Please note that nothing is done to the Certificates and CRLs in the message - this
+ * is entirely the responsibility of our callers.
+ */
+SECStatus
+NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
+{
+ NSSCMSSignerInfo **signerinfos, *signerinfo;
+ NSSCMSContentInfo *cinfo;
+ SECOidTag digestalgtag;
+ SECStatus ret = SECFailure;
+ SECStatus rv;
+ SECItem *contentType;
+ int certcount;
+ int i, ci, cli, n, rci, si;
+ PLArenaPool *poolp;
+ CERTCertificateList *certlist;
+ extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ poolp = sigd->cmsg->poolp;
+ cinfo = &(sigd->contentInfo);
+
+ /* did we have digest calculation going on? */
+ if (cinfo->privateInfo && cinfo->privateInfo->digcx) {
+ rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp,
+ &(sigd->digests));
+ /* error has been set by NSS_CMSDigestContext_FinishMultiple */
+ cinfo->privateInfo->digcx = NULL;
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ signerinfos = sigd->signerInfos;
+ certcount = 0;
+
+ /* prepare all the SignerInfos (there may be none) */
+ for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
+ signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
+
+ /* find correct digest for this signerinfo */
+ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
+ n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
+ if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
+ /* oops - digest not found */
+ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
+ goto loser;
+ }
+
+ /* XXX if our content is anything else but data, we need to force the
+ * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
+ * collection...") */
+
+ /* pass contentType here as we want a contentType attribute */
+ if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
+ goto loser;
+
+ /* sign the thing */
+ rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* while we're at it, count number of certs in certLists */
+ certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
+ if (certlist)
+ certcount += certlist->len;
+ }
+
+ /* this is a SET OF, so we need to sort them guys */
+ rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /*
+ * now prepare certs & crls
+ */
+
+ /* count the rest of the certs */
+ if (sigd->certs != NULL) {
+ for (ci = 0; sigd->certs[ci] != NULL; ci++)
+ certcount++;
+ }
+
+ if (sigd->certLists != NULL) {
+ for (cli = 0; sigd->certLists[cli] != NULL; cli++)
+ certcount += sigd->certLists[cli]->len;
+ }
+
+ if (certcount == 0) {
+ sigd->rawCerts = NULL;
+ } else {
+ /*
+ * Combine all of the certs and cert chains into rawcerts.
+ * Note: certcount is an upper bound; we may not need that many slots
+ * but we will allocate anyway to avoid having to do another pass.
+ * (The temporary space saving is not worth it.)
+ *
+ * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
+ * SetOfDERcertficates implementation
+ */
+ sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
+ if (sigd->rawCerts == NULL)
+ return SECFailure;
+
+ /*
+ * XXX Want to check for duplicates and not add *any* cert that is
+ * already in the set. This will be more important when we start
+ * dealing with larger sets of certs, dual-key certs (signing and
+ * encryption), etc. For the time being we can slide by...
+ *
+ * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
+ * SetOfDERcertficates implementation
+ */
+ rci = 0;
+ if (signerinfos != NULL) {
+ for (si = 0; signerinfos[si] != NULL; si++) {
+ signerinfo = signerinfos[si];
+ for (ci = 0; ci < signerinfo->certList->len; ci++)
+ sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
+ }
+ }
+
+ if (sigd->certs != NULL) {
+ for (ci = 0; sigd->certs[ci] != NULL; ci++)
+ sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
+ }
+
+ if (sigd->certLists != NULL) {
+ for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
+ for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
+ sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
+ }
+ }
+
+ sigd->rawCerts[rci] = NULL;
+
+ /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
+ NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
+ }
+
+ ret = SECSuccess;
+
+loser:
+ return ret;
+}
+
+SECStatus
+NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
+{
+ SECStatus rv;
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* handle issue with Windows 2003 servers and kerberos */
+ if (sigd->digestAlgorithms != NULL) {
+ int i;
+ for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
+ SECAlgorithmID *algid = sigd->digestAlgorithms[i];
+ SECOidTag senttag = SECOID_FindOIDTag(&algid->algorithm);
+ SECOidTag maptag = NSS_CMSUtil_MapSignAlgs(senttag);
+
+ if (maptag != senttag) {
+ SECOidData *hashoid = SECOID_FindOIDByTag(maptag);
+ rv = SECITEM_CopyItem(sigd->cmsg->poolp, &algid->algorithm, &hashoid->oid);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ /* set up the digests */
+ if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
+ /* if digests are already there, do nothing */
+ sigd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
+ if (sigd->contentInfo.privateInfo->digcx == NULL)
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a
+ * SignedData after all the encapsulated data was passed through the decoder.
+ */
+SECStatus
+NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
+{
+ SECStatus rv = SECSuccess;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* did we have digest calculation going on? */
+ if (sigd->contentInfo.privateInfo && sigd->contentInfo.privateInfo->digcx) {
+ rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.privateInfo->digcx,
+ sigd->cmsg->poolp, &(sigd->digests));
+ /* error set by NSS_CMSDigestContext_FinishMultiple */
+ sigd->contentInfo.privateInfo->digcx = NULL;
+ }
+ return rv;
+}
+
+/*
+ * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
+ * after all decoding is finished.
+ */
+SECStatus
+NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
+{
+ NSSCMSSignerInfo **signerinfos = NULL;
+ int i;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* set cmsg for all the signerinfos */
+ signerinfos = sigd->signerInfos;
+
+ /* set cmsg for all the signerinfos */
+ if (signerinfos) {
+ for (i = 0; signerinfos[i] != NULL; i++)
+ signerinfos[i]->cmsg = sigd->cmsg;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
+ */
+NSSCMSSignerInfo **
+NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ return sigd->signerInfos;
+}
+
+int
+NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return 0;
+ }
+ return NSS_CMSArray_Count((void **)sigd->signerInfos);
+}
+
+NSSCMSSignerInfo *
+NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
+{
+ if (!sigd || !sigd->signerInfos) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ return sigd->signerInfos[i];
+}
+
+/*
+ * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
+ */
+SECAlgorithmID **
+NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ return sigd->digestAlgorithms;
+}
+
+/*
+ * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
+ */
+NSSCMSContentInfo *
+NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ return &(sigd->contentInfo);
+}
+
+/*
+ * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
+ */
+SECItem **
+NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ return sigd->rawCerts;
+}
+
+SECStatus
+NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
+ SECCertUsage certusage, PRBool keepcerts)
+{
+ int certcount;
+ CERTCertificate **certArray = NULL;
+ CERTCertList *certList = NULL;
+ CERTCertListNode *node;
+ SECStatus rv;
+ SECItem **rawArray;
+ int i;
+ PRTime now;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
+
+ /* get the certs in the temp DB */
+ rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
+ &certArray, PR_FALSE, PR_FALSE, NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* save the certs so they don't get destroyed */
+ for (i = 0; i < certcount; i++) {
+ CERTCertificate *cert = certArray[i];
+ if (cert)
+ NSS_CMSSignedData_AddTempCertificate(sigd, cert);
+ }
+
+ if (!keepcerts) {
+ goto done;
+ }
+
+ /* build a CertList for filtering */
+ certList = CERT_NewCertList();
+ if (certList == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ for (i = 0; i < certcount; i++) {
+ CERTCertificate *cert = certArray[i];
+ if (cert)
+ cert = CERT_DupCertificate(cert);
+ if (cert)
+ CERT_AddCertToListTail(certList, cert);
+ }
+
+ /* filter out the certs we don't want */
+ rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* go down the remaining list of certs and verify that they have
+ * valid chains, then import them.
+ */
+ now = PR_Now();
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificateList *certChain;
+
+ if (CERT_VerifyCert(certdb, node->cert,
+ PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
+ continue;
+ }
+
+ certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
+ if (!certChain) {
+ continue;
+ }
+
+ /*
+ * CertChain returns an array of SECItems, import expects an array of
+ * SECItem pointers. Create the SECItem Pointers from the array of
+ * SECItems.
+ */
+ rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *));
+ if (!rawArray) {
+ CERT_DestroyCertificateList(certChain);
+ continue;
+ }
+ for (i = 0; i < certChain->len; i++) {
+ rawArray[i] = &certChain->certs[i];
+ }
+ (void)CERT_ImportCerts(certdb, certusage, certChain->len,
+ rawArray, NULL, keepcerts, PR_FALSE, NULL);
+ PORT_Free(rawArray);
+ CERT_DestroyCertificateList(certChain);
+ }
+
+ rv = SECSuccess;
+
+ /* XXX CRL handling */
+
+done:
+ if (sigd->signerInfos != NULL) {
+ /* fill in all signerinfo's certs */
+ for (i = 0; sigd->signerInfos[i] != NULL; i++)
+ (void)NSS_CMSSignerInfo_GetSigningCertificate(
+ sigd->signerInfos[i], certdb);
+ }
+
+loser:
+ /* now free everything */
+ if (certArray) {
+ CERT_DestroyCertArray(certArray, certcount);
+ }
+ if (certList) {
+ CERT_DestroyCertList(certList);
+ }
+
+ return rv;
+}
+
+/*
+ * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
+ * of external signatures!
+ */
+
+/*
+ * NSS_CMSSignedData_VerifySignerInfo - check the signatures.
+ *
+ * The digests were either calculated during decoding (and are stored in the
+ * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
+ *
+ * The verification checks if the signing cert is valid and has a trusted chain
+ * for the purpose specified by "certusage".
+ */
+SECStatus
+NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i,
+ CERTCertDBHandle *certdb, SECCertUsage certusage)
+{
+ NSSCMSSignerInfo *signerinfo;
+ NSSCMSContentInfo *cinfo;
+ SECOidData *algiddata;
+ SECItem *contentType, *digest;
+ SECOidTag oidTag;
+ SECStatus rv;
+
+ if (!sigd || !sigd->signerInfos) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ cinfo = &(sigd->contentInfo);
+
+ signerinfo = sigd->signerInfos[i];
+
+ /* verify certificate */
+ rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage);
+ if (rv != SECSuccess)
+ return rv; /* error is set */
+
+ /* find digest and contentType for signerinfo */
+ algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
+ oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN;
+ digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag);
+ /* NULL digest is acceptable. */
+ contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
+ /* NULL contentType is acceptable. */
+
+ /* now verify signature */
+ rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
+ return rv;
+}
+
+/*
+ * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
+ */
+SECStatus
+NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd,
+ CERTCertDBHandle *certdb,
+ SECCertUsage usage)
+{
+ CERTCertificate *cert;
+ SECStatus rv = SECSuccess;
+ int i;
+ int count;
+ PRTime now;
+ void *pwarg = NULL;
+
+ if (!sigd || !certdb || !sigd->rawCerts) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ count = NSS_CMSArray_Count((void **)sigd->rawCerts);
+ now = PR_Now();
+ for (i = 0; i < count; i++) {
+ if (sigd->certs && sigd->certs[i]) {
+ cert = CERT_DupCertificate(sigd->certs[i]);
+ } else {
+ cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]);
+ if (!cert) {
+ rv = SECFailure;
+ break;
+ }
+ }
+ if (sigd->cmsg) {
+ pwarg = sigd->cmsg->pwfn_arg;
+ }
+ rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now,
+ pwarg, NULL);
+ CERT_DestroyCertificate(cert);
+ }
+
+ return rv;
+}
+
+/*
+ * NSS_CMSSignedData_HasDigests - see if we have digests in place
+ */
+PRBool
+NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return PR_FALSE;
+ }
+ return (sigd->digests != NULL);
+}
+
+SECStatus
+NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
+{
+ SECStatus rv;
+
+ if (!sigd || !certlist) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
+ rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
+
+ return rv;
+}
+
+/*
+ * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
+ */
+SECStatus
+NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
+{
+ CERTCertificateList *certlist;
+ SECCertUsage usage;
+ SECStatus rv;
+
+ usage = certUsageEmailSigner;
+
+ if (!sigd || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* do not include root */
+ certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
+ if (certlist == NULL)
+ return SECFailure;
+
+ rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
+
+ return rv;
+}
+
+extern SECStatus
+NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
+{
+ CERTCertificate *c;
+ SECStatus rv;
+
+ if (!sigd || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ c = CERT_DupCertificate(cert);
+ rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c);
+ return rv;
+}
+
+SECStatus
+NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
+{
+ CERTCertificate *c;
+ SECStatus rv;
+
+ if (!sigd || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ c = CERT_DupCertificate(cert);
+ rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
+ return rv;
+}
+
+PRBool
+NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
+{
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return PR_FALSE;
+ }
+ if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
+ return PR_TRUE;
+ else if (sigd->crls != NULL && sigd->crls[0] != NULL)
+ return PR_TRUE;
+ else
+ return PR_FALSE;
+}
+
+SECStatus
+NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
+ NSSCMSSignerInfo *signerinfo)
+{
+ void *mark;
+ SECStatus rv;
+ SECOidTag digestalgtag;
+ PLArenaPool *poolp;
+
+ if (!sigd || !signerinfo) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ poolp = sigd->cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* add signerinfo */
+ rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /*
+ * add empty digest
+ * Empty because we don't have it yet. Either it gets created during encoding
+ * (if the data is present) or has to be set externally.
+ * XXX maybe pass it in optionally?
+ */
+ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
+ rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /*
+ * The last thing to get consistency would be adding the digest.
+ */
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSSignedData_SetDigests - set a signedData's digests member
+ *
+ * "digestalgs" - array of digest algorithm IDs
+ * "digests" - array of digests corresponding to the digest algorithms
+ */
+SECStatus
+NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
+ SECAlgorithmID **digestalgs,
+ SECItem **digests)
+{
+ int cnt, i, idx;
+
+ if (!sigd || !digestalgs || !digests) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (sigd->digestAlgorithms == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* we assume that the digests array is just not there yet */
+ PORT_Assert(sigd->digests == NULL);
+ if (sigd->digests != NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ /* now allocate one (same size as digestAlgorithms) */
+ cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
+ sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
+ if (sigd->digests == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
+ /* try to find the sigd's i'th digest algorithm in the array we passed in */
+ idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
+ if (idx < 0) {
+ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
+ return SECFailure;
+ }
+ if (!digests[idx]) {
+ /* We have no digest for this algorithm, probably because it is
+ ** unrecognized or unsupported. We'll ignore this here. If this
+ ** digest is needed later, an error will be be generated then.
+ */
+ continue;
+ }
+
+ /* found it - now set it */
+ if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
+ SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ }
+ return SECSuccess;
+}
+
+SECStatus
+NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
+ SECOidTag digestalgtag,
+ SECItem *digestdata)
+{
+ SECItem *digest = NULL;
+ PLArenaPool *poolp;
+ void *mark;
+ int n, cnt;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ poolp = sigd->cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ if (digestdata) {
+ digest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
+
+ /* copy digestdata item to arena (in case we have it and are not only making room) */
+ if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
+ goto loser;
+ }
+
+ /* now allocate one (same size as digestAlgorithms) */
+ if (sigd->digests == NULL) {
+ cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
+ sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
+ if (sigd->digests == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ }
+
+ n = -1;
+ if (sigd->digestAlgorithms != NULL)
+ n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
+
+ /* if not found, add a digest */
+ if (n < 0) {
+ if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
+ goto loser;
+ } else {
+ /* replace NULL pointer with digest item (and leak previous value) */
+ sigd->digests[n] = digest;
+ }
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSSignedData_AddDigest(PLArenaPool *poolp,
+ NSSCMSSignedData *sigd,
+ SECOidTag digestalgtag,
+ SECItem *digest)
+{
+ SECAlgorithmID *digestalg;
+ void *mark;
+
+ if (!sigd || !poolp) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(poolp);
+
+ digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
+ if (digestalg == NULL)
+ goto loser;
+
+ if (SECOID_SetAlgorithmID(poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
+ goto loser;
+
+ if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms),
+ (void *)digestalg) != SECSuccess ||
+ /* even if digest is NULL, add dummy to have same-size array */
+ NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests),
+ (void *)digest) != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/* XXX This function doesn't set the error code on failure. */
+SECItem *
+NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
+{
+ int n;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) {
+ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
+ return NULL;
+ }
+
+ n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
+
+ return (n < 0) ? NULL : sigd->digests[n];
+}
+
+/* =============================================================================
+ * Misc. utility functions
+ */
+
+/*
+ * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
+ *
+ * cert - base certificates that will be included
+ * include_chain - if true, include the complete cert chain for cert
+ *
+ * More certs and chains can be added via AddCertificate and AddCertChain.
+ *
+ * An error results in a return value of NULL and an error set.
+ *
+ * XXXX CRLs
+ */
+NSSCMSSignedData *
+NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
+{
+ NSSCMSSignedData *sigd;
+ void *mark;
+ PLArenaPool *poolp;
+ SECStatus rv;
+
+ if (!cmsg || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ poolp = cmsg->poolp;
+ mark = PORT_ArenaMark(poolp);
+
+ sigd = NSS_CMSSignedData_Create(cmsg);
+ if (sigd == NULL)
+ goto loser;
+
+ /* no signerinfos, thus no digestAlgorithms */
+
+ /* but certs */
+ if (include_chain) {
+ rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
+ } else {
+ rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
+ }
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* RFC2630 5.2 sez:
+ * In the degenerate case where there are no signers, the
+ * EncapsulatedContentInfo value being "signed" is irrelevant. In this
+ * case, the content type within the EncapsulatedContentInfo value being
+ * "signed" should be id-data (as defined in section 4), and the content
+ * field of the EncapsulatedContentInfo value should be omitted.
+ */
+ rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return sigd;
+
+loser:
+ if (sigd)
+ NSS_CMSSignedData_Destroy(sigd);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/* TODO:
+ * NSS_CMSSignerInfo_GetReceiptRequest()
+ * NSS_CMSSignedData_HasReceiptRequest()
+ * easy way to iterate over signers
+ */
diff --git a/security/nss/lib/smime/cmssiginfo.c b/security/nss/lib/smime/cmssiginfo.c
new file mode 100644
index 0000000000..ed966f889f
--- /dev/null
+++ b/security/nss/lib/smime/cmssiginfo.c
@@ -0,0 +1,1081 @@
+/* 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 signerInfo methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+#include "secder.h"
+#include "cryptohi.h"
+
+#include "smime.h"
+
+/* =============================================================================
+ * SIGNERINFO
+ */
+NSSCMSSignerInfo *
+nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
+ CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
+ SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
+
+NSSCMSSignerInfo *
+NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID,
+ SECKEYPublicKey *pubKey,
+ SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
+{
+ return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL,
+ subjKeyID, pubKey, signingKey, digestalgtag);
+}
+
+NSSCMSSignerInfo *
+NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
+{
+ return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL,
+ NULL, NULL, digestalgtag);
+}
+
+NSSCMSSignerInfo *
+nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
+ CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
+ SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
+{
+ void *mark;
+ NSSCMSSignerInfo *signerinfo;
+ int version;
+ PLArenaPool *poolp;
+ SECStatus rv;
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
+ if (signerinfo == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ signerinfo->cmsg = cmsg;
+
+ switch (type) {
+ case NSSCMSSignerID_IssuerSN:
+ signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
+ if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
+ goto loser;
+ if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
+ goto loser;
+ break;
+ case NSSCMSSignerID_SubjectKeyID:
+ signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
+ PORT_Assert(subjKeyID);
+ if (!subjKeyID)
+ goto loser;
+
+ signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
+ rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
+ subjKeyID);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
+ if (!signerinfo->signingKey)
+ goto loser;
+ signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
+ if (!signerinfo->pubKey)
+ goto loser;
+ break;
+ default:
+ goto loser;
+ }
+
+ /* set version right now */
+ version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
+ /* RFC2630 5.3 "version is the syntax version number. If the .... " */
+ if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
+ version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
+ (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
+
+ if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return signerinfo;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/*
+ * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
+ */
+void
+NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
+{
+ if (si->cert != NULL)
+ CERT_DestroyCertificate(si->cert);
+
+ if (si->certList != NULL)
+ CERT_DestroyCertificateList(si->certList);
+
+ /* XXX storage ??? */
+}
+static SECOidTag
+NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,
+ SECOidTag pubkAlgTag,
+ SECOidTag signAlgTag)
+{
+ switch (keyType) {
+ case rsaKey:
+ return pubkAlgTag;
+ case rsaPssKey:
+ case dsaKey:
+ case ecKey:
+ return signAlgTag;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+}
+
+/*
+ * NSS_CMSSignerInfo_Sign - sign something
+ *
+ */
+SECStatus
+NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest,
+ SECItem *contentType)
+{
+ CERTCertificate *cert;
+ SECKEYPrivateKey *privkey = NULL;
+ SECOidTag digestalgtag;
+ SECOidTag pubkAlgTag;
+ SECOidTag signAlgTag;
+ SECOidTag cmsSignAlgTag;
+ SECItem signature = { 0 };
+ SECStatus rv;
+ PLArenaPool *poolp, *tmppoolp = NULL;
+ SECAlgorithmID *algID, freeAlgID;
+ CERTSubjectPublicKeyInfo *spki;
+
+ PORT_Assert(digest != NULL);
+
+ poolp = signerinfo->cmsg->poolp;
+
+ switch (signerinfo->signerIdentifier.identifierType) {
+ case NSSCMSSignerID_IssuerSN:
+ cert = signerinfo->cert;
+
+ privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
+ if (privkey == NULL)
+ goto loser;
+ algID = &cert->subjectPublicKeyInfo.algorithm;
+ break;
+ case NSSCMSSignerID_SubjectKeyID:
+ privkey = signerinfo->signingKey;
+ signerinfo->signingKey = NULL;
+ spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
+ SECKEY_DestroyPublicKey(signerinfo->pubKey);
+ signerinfo->pubKey = NULL;
+ SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ algID = &freeAlgID;
+ break;
+ default:
+ goto loser;
+ }
+ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
+ /*
+ * XXX I think there should be a cert-level interface for this,
+ * so that I do not have to know about subjectPublicKeyInfo...
+ */
+ pubkAlgTag = SECOID_GetAlgorithmTag(algID);
+ if (algID == &freeAlgID) {
+ SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
+ }
+
+ signAlgTag = SEC_GetSignatureAlgorithmOidTag(SECKEY_GetPrivateKeyType(privkey),
+ digestalgtag);
+ if (signAlgTag == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(
+ SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag);
+ if (cmsSignAlgTag == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
+ cmsSignAlgTag, NULL) != SECSuccess)
+ goto loser;
+
+ if (signerinfo->authAttr != NULL) {
+ SECItem encoded_attrs;
+
+ /* find and fill in the message digest attribute. */
+ rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
+ SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (contentType != NULL) {
+ /* if the caller wants us to, find and fill in the content type attribute. */
+ rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
+ SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ if ((tmppoolp = PORT_NewArena(1024)) == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /*
+ * Before encoding, reorder the attributes so that when they
+ * are encoded, they will be conforming DER, which is required
+ * to have a specific order and that is what must be used for
+ * the hash/signature. We do this here, rather than building
+ * it into EncodeAttributes, because we do not want to do
+ * such reordering on incoming messages (which also uses
+ * EncodeAttributes) or our old signatures (and other "broken"
+ * implementations) will not verify. So, we want to guarantee
+ * that we send out good DER encodings of attributes, but not
+ * to expect to receive them.
+ */
+ if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
+ goto loser;
+
+ encoded_attrs.data = NULL;
+ encoded_attrs.len = 0;
+ if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr),
+ &encoded_attrs) == NULL)
+ goto loser;
+
+ rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len,
+ privkey, signAlgTag);
+ PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
+ tmppoolp = 0;
+ } else {
+ rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
+ }
+ SECKEY_DestroyPrivateKey(privkey);
+ privkey = NULL;
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
+ goto loser;
+
+ SECITEM_FreeItem(&signature, PR_FALSE);
+
+ return SECSuccess;
+
+loser:
+ if (signature.len != 0)
+ SECITEM_FreeItem(&signature, PR_FALSE);
+ if (privkey)
+ SECKEY_DestroyPrivateKey(privkey);
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
+ SECCertUsage certusage)
+{
+ CERTCertificate *cert;
+ PRTime stime;
+
+ if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
+ signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
+ return SECFailure;
+ }
+
+ /*
+ * Get and convert the signing time; if available, it will be used
+ * both on the cert verification and for importing the sender
+ * email profile.
+ */
+ if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess)
+ stime = PR_Now(); /* not found or conversion failed, so check against now */
+
+ /*
+ * XXX This uses the signing time, if available. Additionally, we
+ * might want to, if there is no signing time, get the message time
+ * from the mail header itself, and use that. That would require
+ * a change to our interface though, and for S/MIME callers to pass
+ * in a time (and for non-S/MIME callers to pass in nothing, or
+ * maybe make them pass in the current time, always?).
+ */
+ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime,
+ signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
+ signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
+ *
+ * Just verifies the signature. The assumption is that verification of
+ * the certificate is done already.
+ */
+SECStatus
+NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo,
+ SECItem *digest, /* may be NULL */
+ SECItem *contentType) /* may be NULL */
+{
+ SECKEYPublicKey *publickey = NULL;
+ NSSCMSAttribute *attr;
+ SECItem encoded_attrs;
+ CERTCertificate *cert;
+ NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
+ PLArenaPool *poolp;
+ SECOidTag digestalgtag;
+ SECOidTag pubkAlgTag;
+ SECOidTag digestalgtagCmp;
+ SECOidTag sigAlgTag;
+
+ if (signerinfo == NULL)
+ return SECFailure;
+
+ /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL
+ ** and cert has not been verified
+ */
+ cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
+ if (cert == NULL) {
+ vs = NSSCMSVS_SigningCertNotFound;
+ goto loser;
+ }
+
+ if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
+ vs = NSSCMSVS_ProcessingError;
+ goto loser;
+ }
+
+ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
+ pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
+ sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
+ if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) ||
+ (sigAlgTag == SEC_OID_UNKNOWN)) {
+ vs = NSSCMSVS_SignatureAlgorithmUnknown;
+ goto loser;
+ }
+
+ if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
+ if (contentType) {
+ /*
+ * Check content type
+ *
+ * RFC2630 sez that if there are any authenticated attributes,
+ * then there must be one for content type which matches the
+ * content type of the content being signed, and there must
+ * be one for message digest which matches our message digest.
+ * So check these things first.
+ */
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+ SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
+ if (attr == NULL) {
+ vs = NSSCMSVS_MalformedSignature;
+ goto loser;
+ }
+
+ if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
+ vs = NSSCMSVS_MalformedSignature;
+ goto loser;
+ }
+ }
+
+ /*
+ * Check digest
+ */
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+ SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
+ if (attr == NULL) {
+ vs = NSSCMSVS_MalformedSignature;
+ goto loser;
+ }
+ if (!digest ||
+ NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
+ vs = NSSCMSVS_DigestMismatch;
+ goto loser;
+ }
+
+ if ((poolp = PORT_NewArena(1024)) == NULL) {
+ vs = NSSCMSVS_ProcessingError;
+ goto loser;
+ }
+
+ /*
+ * Check signature
+ *
+ * The signature is based on a digest of the DER-encoded authenticated
+ * attributes. So, first we encode and then we digest/verify.
+ * we trust the decoder to have the attributes in the right (sorted)
+ * order
+ */
+ encoded_attrs.data = NULL;
+ encoded_attrs.len = 0;
+
+ if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr),
+ &encoded_attrs) == NULL ||
+ encoded_attrs.data == NULL || encoded_attrs.len == 0) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ vs = NSSCMSVS_ProcessingError;
+ goto loser;
+ }
+
+ if (sigAlgTag == pubkAlgTag) {
+ /* This is to handle cases in which signatureAlgorithm field
+ * specifies the public key algorithm rather than a signature
+ * algorithm. */
+ vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
+ publickey, &(signerinfo->encDigest), pubkAlgTag,
+ digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess)
+ ? NSSCMSVS_BadSignature
+ : NSSCMSVS_GoodSignature;
+ } else {
+ if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data,
+ encoded_attrs.len, publickey, &(signerinfo->encDigest),
+ &(signerinfo->digestEncAlg), &digestalgtagCmp,
+ signerinfo->cmsg->pwfn_arg) != SECSuccess) {
+ vs = NSSCMSVS_BadSignature;
+ } else if (digestalgtagCmp != digestalgtag) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ vs = NSSCMSVS_BadSignature;
+ } else {
+ vs = NSSCMSVS_GoodSignature;
+ }
+ }
+
+ PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
+
+ } else {
+ SECItem *sig;
+
+ /* No authenticated attributes.
+ ** The signature is based on the plain message digest.
+ */
+ sig = &(signerinfo->encDigest);
+ if (sig->len == 0)
+ goto loser;
+
+ if (sigAlgTag == pubkAlgTag) {
+ /* This is to handle cases in which signatureAlgorithm field
+ * specifies the public key algorithm rather than a signature
+ * algorithm. */
+ vs = (!digest ||
+ VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
+ digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess)
+ ? NSSCMSVS_BadSignature
+ : NSSCMSVS_GoodSignature;
+ } else {
+ vs = (!digest ||
+ VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig,
+ &(signerinfo->digestEncAlg), digestalgtag,
+ signerinfo->cmsg->pwfn_arg) != SECSuccess)
+ ? NSSCMSVS_BadSignature
+ : NSSCMSVS_GoodSignature;
+ }
+ }
+
+ if (vs == NSSCMSVS_BadSignature) {
+ int error = PORT_GetError();
+ /*
+ * XXX Change the generic error into our specific one, because
+ * in that case we get a better explanation out of the Security
+ * Advisor. This is really a bug in the PSM error strings (the
+ * "generic" error has a lousy/wrong message associated with it
+ * which assumes the signature verification was done for the
+ * purposes of checking the issuer signature on a certificate)
+ * but this is at least an easy workaround and/or in the
+ * Security Advisor, which specifically checks for the error
+ * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
+ * in that case but does not similarly check for
+ * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
+ * probably say the wrong thing in the case that it *was* the
+ * certificate signature check that failed during the cert
+ * verification done above. Our error handling is really a mess.
+ */
+ if (error == SEC_ERROR_BAD_SIGNATURE)
+ PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ /*
+ * map algorithm failures to NSSCMSVS values
+ */
+ if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
+ (error == SEC_ERROR_INVALID_ALGORITHM)) {
+ /* keep the same error code as 3.11 and before */
+ PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ vs = NSSCMSVS_SignatureAlgorithmUnsupported;
+ }
+ }
+
+ if (publickey != NULL)
+ SECKEY_DestroyPublicKey(publickey);
+
+ signerinfo->verificationStatus = vs;
+
+ return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
+
+loser:
+ if (publickey != NULL)
+ SECKEY_DestroyPublicKey(publickey);
+
+ signerinfo->verificationStatus = vs;
+
+ PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ return SECFailure;
+}
+
+NSSCMSVerificationStatus
+NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
+{
+ return signerinfo->verificationStatus;
+}
+
+SECOidData *
+NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
+{
+ SECOidData *algdata;
+ SECOidTag algtag;
+
+ algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm));
+ if (algdata == NULL) {
+ return algdata;
+ }
+ /* Windows may have given us a signer algorithm oid instead of a digest
+ * algorithm oid. This call will map to a signer oid to a digest one,
+ * otherwise it leaves the oid alone and let the chips fall as they may
+ * if it's not a digest oid.
+ */
+ algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
+ if (algtag != algdata->offset) {
+ /* if the tags don't match, then we must have received a signer
+ * algorithID. Now we need to get the oid data for the digest
+ * oid, which the rest of the code is expecting */
+ algdata = SECOID_FindOIDByTag(algtag);
+ }
+
+ return algdata;
+}
+
+SECOidTag
+NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
+{
+ SECOidData *algdata;
+
+ if (!signerinfo) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SEC_OID_UNKNOWN;
+ }
+
+ algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
+ if (algdata != NULL)
+ return algdata->offset;
+ else
+ return SEC_OID_UNKNOWN;
+}
+
+CERTCertificateList *
+NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
+{
+ return signerinfo->certList;
+}
+
+int
+NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
+{
+ unsigned long version;
+
+ /* always take apart the SECItem */
+ if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
+ return 0;
+ else
+ return (int)version;
+}
+
+/*
+ * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
+ * in UTCTime or GeneralizedTime format,
+ * of a CMS signerInfo.
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to XXXX (what?)
+ * A return value of NULL is an error.
+ */
+SECStatus
+NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
+{
+ NSSCMSAttribute *attr;
+ SECItem *value;
+
+ if (sinfo == NULL)
+ return SECFailure;
+
+ if (sinfo->signingTime != 0) {
+ *stime = sinfo->signingTime; /* cached copy */
+ return SECSuccess;
+ }
+
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr,
+ SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
+ /* XXXX multi-valued attributes NIH */
+ if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
+ return SECFailure;
+ if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
+ return SECFailure;
+ sinfo->signingTime = *stime; /* make cached copy */
+ return SECSuccess;
+}
+
+/*
+ * Return the signing cert of a CMS signerInfo.
+ *
+ * the certs in the enclosing SignedData must have been imported already
+ */
+CERTCertificate *
+NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
+{
+ CERTCertificate *cert;
+ NSSCMSSignerIdentifier *sid;
+
+ if (signerinfo->cert != NULL)
+ return signerinfo->cert;
+
+ /* no certdb, and cert hasn't been set yet? */
+ if (certdb == NULL)
+ return NULL;
+
+ /*
+ * This cert will also need to be freed, but since we save it
+ * in signerinfo for later, we do not want to destroy it when
+ * we leave this function -- we let the clean-up of the entire
+ * cinfo structure later do the destroy of this cert.
+ */
+ sid = &signerinfo->signerIdentifier;
+ switch (sid->identifierType) {
+ case NSSCMSSignerID_IssuerSN:
+ cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
+ break;
+ case NSSCMSSignerID_SubjectKeyID:
+ cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
+ break;
+ default:
+ cert = NULL;
+ break;
+ }
+
+ /* cert can be NULL at that point */
+ signerinfo->cert = cert; /* earmark it */
+
+ return cert;
+}
+
+/*
+ * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to allocated memory, which must be freed with PORT_Free.
+ * A return value of NULL is an error.
+ */
+char *
+NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
+{
+ CERTCertificate *signercert;
+
+ /* will fail if cert is not verified */
+ if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
+ return NULL;
+
+ return (CERT_GetCommonName(&signercert->subject));
+}
+
+/*
+ * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
+ *
+ * sinfo - signerInfo data for this signer
+ *
+ * Returns a pointer to allocated memory, which must be freed.
+ * A return value of NULL is an error.
+ */
+char *
+NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
+{
+ CERTCertificate *signercert;
+
+ if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
+ return NULL;
+
+ if (!signercert->emailAddr || !signercert->emailAddr[0])
+ return NULL;
+
+ return (PORT_Strdup(signercert->emailAddr));
+}
+
+/*
+ * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ */
+SECStatus
+NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
+{
+ return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
+}
+
+/*
+ * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
+ * unauthenticated attributes of "signerinfo".
+ */
+SECStatus
+NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
+{
+ return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
+}
+
+/*
+ * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * 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 do nothing.
+ *
+ * XXX This will probably just shove the current time into "signerinfo"
+ * but it will not actually get signed until the entire item is
+ * processed for encoding. Is this (expected to be small) delay okay?
+ */
+SECStatus
+NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
+{
+ NSSCMSAttribute *attr;
+ SECItem stime;
+ void *mark;
+ PLArenaPool *poolp;
+
+ poolp = signerinfo->cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* create new signing time attribute */
+ if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
+ goto loser;
+
+ if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
+ SECITEM_FreeItem(&stime, PR_FALSE);
+ goto loser;
+ }
+
+ SECITEM_FreeItem(&stime, PR_FALSE);
+
+ if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed
+ * messages for email (S/MIME).
+ */
+SECStatus
+NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
+{
+ NSSCMSAttribute *attr;
+ SECItem *smimecaps = NULL;
+ void *mark;
+ PLArenaPool *poolp;
+
+ poolp = signerinfo->cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
+ if (smimecaps == NULL)
+ goto loser;
+
+ /* create new signing time attribute */
+ if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
+ goto loser;
+
+ if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
+ goto loser;
+
+ if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed messages for email (S/MIME).
+ */
+SECStatus
+NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
+{
+ NSSCMSAttribute *attr;
+ SECItem *smimeekp = NULL;
+ void *mark;
+ PLArenaPool *poolp;
+
+ /* verify this cert for encryption */
+ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
+ return SECFailure;
+ }
+
+ poolp = signerinfo->cmsg->poolp;
+ mark = PORT_ArenaMark(poolp);
+
+ smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
+ if (smimeekp == NULL)
+ goto loser;
+
+ /* create new signing time attribute */
+ if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
+ goto loser;
+
+ if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
+ goto loser;
+
+ if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
+ *
+ * This is expected to be included in outgoing signed messages for email (S/MIME),
+ * if compatibility with Microsoft mail clients is wanted.
+ */
+SECStatus
+NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
+{
+ NSSCMSAttribute *attr;
+ SECItem *smimeekp = NULL;
+ void *mark;
+ PLArenaPool *poolp;
+
+ /* verify this cert for encryption */
+ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
+ return SECFailure;
+ }
+
+ poolp = signerinfo->cmsg->poolp;
+ mark = PORT_ArenaMark(poolp);
+
+ smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
+ if (smimeekp == NULL)
+ goto loser;
+
+ /* create new signing time attribute */
+ if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
+ goto loser;
+
+ if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
+ goto loser;
+
+ if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
+ goto loser;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+/*
+ * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
+ *
+ * 1. digest the DER-encoded signature value of the original signerinfo
+ * 2. create new signerinfo with correct version, sid, digestAlg
+ * 3. add message-digest authAttr, but NO content-type
+ * 4. sign the authAttrs
+ * 5. DER-encode the new signerInfo
+ * 6. add the whole thing to original signerInfo's unAuthAttrs
+ * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
+ *
+ * XXXX give back the new signerinfo?
+ */
+SECStatus
+NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
+ SECOidTag digestalg, CERTCertificate signingcert)
+{
+ /* XXXX TBD XXXX */
+ return SECFailure;
+}
+
+/*
+ * XXXX the following needs to be done in the S/MIME layer code
+ * after signature of a signerinfo is verified
+ */
+SECStatus
+NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
+{
+ CERTCertificate *cert = NULL;
+ SECItem *profile = NULL;
+ NSSCMSAttribute *attr;
+ SECItem *stime = NULL;
+ SECItem *ekp;
+ CERTCertDBHandle *certdb;
+ int save_error;
+ SECStatus rv;
+ PRBool must_free_cert = PR_FALSE;
+
+ certdb = CERT_GetDefaultCertDB();
+
+ /* sanity check - see if verification status is ok (unverified does not count...) */
+ if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
+ return SECFailure;
+
+ /* find preferred encryption cert */
+ if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
+ (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+ SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
+ ekp = NSS_CMSAttribute_GetValue(attr);
+ if (ekp == NULL)
+ return SECFailure;
+
+ /* we assume that all certs coming with the message have been imported to the */
+ /* temporary database */
+ cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
+ if (cert == NULL)
+ return SECFailure;
+ must_free_cert = PR_TRUE;
+ }
+
+ if (cert == NULL) {
+ /* no preferred cert found?
+ * find the cert the signerinfo is signed with instead */
+ cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
+ if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
+ return SECFailure;
+ }
+
+/* verify this cert for encryption (has been verified for signing so far) */
+/* don't verify this cert for encryption. It may just be a signing cert.
+ * that's OK, we can still save the S/MIME profile. The encryption cert
+ * should have already been saved */
+#ifdef notdef
+ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
+ if (must_free_cert)
+ CERT_DestroyCertificate(cert);
+ return SECFailure;
+ }
+#endif
+
+ /* XXX store encryption cert permanently? */
+
+ /*
+ * Remember the current error set because we do not care about
+ * anything set by the functions we are about to call.
+ */
+ save_error = PORT_GetError();
+
+ if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+ SEC_OID_PKCS9_SMIME_CAPABILITIES,
+ PR_TRUE);
+ profile = NSS_CMSAttribute_GetValue(attr);
+ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+ SEC_OID_PKCS9_SIGNING_TIME,
+ PR_TRUE);
+ stime = NSS_CMSAttribute_GetValue(attr);
+ }
+
+ rv = CERT_SaveSMimeProfile(cert, profile, stime);
+ if (must_free_cert)
+ CERT_DestroyCertificate(cert);
+
+ /*
+ * Restore the saved error in case the calls above set a new
+ * one that we do not actually care about.
+ */
+ PORT_SetError(save_error);
+
+ return rv;
+}
+
+/*
+ * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
+ */
+SECStatus
+NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo,
+ NSSCMSCertChainMode cm, SECCertUsage usage)
+{
+ if (signerinfo->cert == NULL)
+ return SECFailure;
+
+ /* don't leak if we get called twice */
+ if (signerinfo->certList != NULL) {
+ CERT_DestroyCertificateList(signerinfo->certList);
+ signerinfo->certList = NULL;
+ }
+
+ switch (cm) {
+ case NSSCMSCM_None:
+ signerinfo->certList = NULL;
+ break;
+ case NSSCMSCM_CertOnly:
+ signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
+ break;
+ case NSSCMSCM_CertChain:
+ signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
+ usage, PR_FALSE);
+ break;
+ case NSSCMSCM_CertChainWithRoot:
+ signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
+ usage, PR_TRUE);
+ break;
+ }
+
+ if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
+ return SECFailure;
+
+ return SECSuccess;
+}
diff --git a/security/nss/lib/smime/cmst.h b/security/nss/lib/smime/cmst.h
new file mode 100644
index 0000000000..3d888bc041
--- /dev/null
+++ b/security/nss/lib/smime/cmst.h
@@ -0,0 +1,491 @@
+/* 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/. */
+
+/*
+ * Header for CMS types.
+ */
+
+#ifndef _CMST_H_
+#define _CMST_H_
+
+#include "seccomon.h"
+#include "secoidt.h"
+#include "certt.h"
+#include "secmodt.h"
+#include "secmodt.h"
+
+#include "plarena.h"
+
+/* Non-opaque objects. NOTE, though: I want them to be treated as
+ * opaque as much as possible. If I could hide them completely,
+ * I would. (I tried, but ran into trouble that was taking me too
+ * much time to get out of.) I still intend to try to do so.
+ * In fact, the only type that "outsiders" should even *name* is
+ * NSSCMSMessage, and they should not reference its fields.
+ */
+/* rjr: PKCS #11 cert handling (pk11cert.c) does use NSSCMSRecipientInfo's.
+ * This is because when we search the recipient list for the cert and key we
+ * want, we need to invert the order of the loops we used to have. The old
+ * loops were:
+ *
+ * For each recipient {
+ * find_cert = PK11_Find_AllCert(recipient->issuerSN);
+ * [which unrolls to... ]
+ * For each slot {
+ * Log into slot;
+ * search slot for cert;
+ * }
+ * }
+ *
+ * the new loop searchs all the recipients at once on a slot. this allows
+ * PKCS #11 to order slots in such a way that logout slots don't get checked
+ * if we can find the cert on a logged in slot. This eliminates lots of
+ * spurious password prompts when smart cards are installed... so why this
+ * comment? If you make NSSCMSRecipientInfo completely opaque, you need
+ * to provide a non-opaque list of issuerSN's (the only field PKCS#11 needs
+ * and fix up pk11cert.c first. NOTE: Only S/MIME calls this special PKCS #11
+ * function.
+ */
+
+typedef struct NSSCMSMessageStr NSSCMSMessage;
+
+typedef union NSSCMSContentUnion NSSCMSContent;
+typedef struct NSSCMSContentInfoStr NSSCMSContentInfo;
+
+typedef struct NSSCMSSignedDataStr NSSCMSSignedData;
+typedef struct NSSCMSSignerInfoStr NSSCMSSignerInfo;
+typedef struct NSSCMSSignerIdentifierStr NSSCMSSignerIdentifier;
+
+typedef struct NSSCMSEnvelopedDataStr NSSCMSEnvelopedData;
+typedef struct NSSCMSOriginatorInfoStr NSSCMSOriginatorInfo;
+typedef struct NSSCMSRecipientInfoStr NSSCMSRecipientInfo;
+
+typedef struct NSSCMSDigestedDataStr NSSCMSDigestedData;
+typedef struct NSSCMSEncryptedDataStr NSSCMSEncryptedData;
+
+typedef struct NSSCMSGenericWrapperDataStr NSSCMSGenericWrapperData;
+
+typedef struct NSSCMSAttributeStr NSSCMSAttribute;
+
+typedef struct NSSCMSDecoderContextStr NSSCMSDecoderContext;
+typedef struct NSSCMSEncoderContextStr NSSCMSEncoderContext;
+
+typedef struct NSSCMSCipherContextStr NSSCMSCipherContext;
+typedef struct NSSCMSDigestContextStr NSSCMSDigestContext;
+
+typedef struct NSSCMSContentInfoPrivateStr NSSCMSContentInfoPrivate;
+
+typedef SECStatus (*NSSCMSGenericWrapperDataCallback)(NSSCMSGenericWrapperData *);
+typedef void (*NSSCMSGenericWrapperDataDestroy)(NSSCMSGenericWrapperData *);
+
+extern const SEC_ASN1Template NSSCMSGenericWrapperDataTemplate[];
+extern const SEC_ASN1Template NSS_PointerToCMSGenericWrapperDataTemplate[];
+
+SEC_ASN1_CHOOSER_DECLARE(NSS_PointerToCMSGenericWrapperDataTemplate)
+SEC_ASN1_CHOOSER_DECLARE(NSSCMSGenericWrapperDataTemplate)
+
+/*
+ * Type of function passed to NSSCMSDecode or NSSCMSDecoderStart.
+ * If specified, this is where the content bytes (only) will be "sent"
+ * as they are recovered during the decoding.
+ * And:
+ * Type of function passed to NSSCMSEncode or NSSCMSEncoderStart.
+ * This is where the DER-encoded bytes will be "sent".
+ *
+ * XXX Should just combine this with NSSCMSEncoderContentCallback type
+ * and use a simpler, common name.
+ */
+typedef void (*NSSCMSContentCallback)(void *arg, const char *buf, unsigned long len);
+
+/*
+ * Type of function passed to NSSCMSDecode or NSSCMSDecoderStart
+ * to retrieve the decryption key. This function is intended to be
+ * used for EncryptedData content info's which do not have a key available
+ * in a certificate, etc.
+ */
+typedef PK11SymKey *(*NSSCMSGetDecryptKeyCallback)(void *arg, SECAlgorithmID *algid);
+
+/* =============================================================================
+ * ENCAPSULATED CONTENTINFO & CONTENTINFO
+ */
+
+union NSSCMSContentUnion {
+ /* either unstructured */
+ SECItem *data;
+ /* or structured data */
+ NSSCMSDigestedData *digestedData;
+ NSSCMSEncryptedData *encryptedData;
+ NSSCMSEnvelopedData *envelopedData;
+ NSSCMSSignedData *signedData;
+ NSSCMSGenericWrapperData *genericData;
+ /* or anonymous pointer to something */
+ void *pointer;
+};
+
+struct NSSCMSContentInfoStr {
+ SECItem contentType;
+ NSSCMSContent content;
+ /* --------- local; not part of encoding --------- */
+ SECOidData *contentTypeTag;
+
+ /* additional info for encryptedData and envelopedData */
+ /* we waste this space for signedData and digestedData. sue me. */
+
+ SECAlgorithmID contentEncAlg;
+ SECItem *rawContent; /* encrypted DER, optional */
+ /* XXXX bytes not encrypted, but encoded? */
+ /* --------- local; not part of encoding --------- */
+ PK11SymKey *bulkkey; /* bulk encryption key */
+ int keysize; /* size of bulk encryption key
+ * (only used by creation code) */
+ SECOidTag contentEncAlgTag; /* oid tag of encryption algorithm
+ * (only used by creation code) */
+ NSSCMSContentInfoPrivate *privateInfo; /* place for NSS private info */
+ void *reserved; /* keep binary compatibility */
+};
+
+/* =============================================================================
+ * MESSAGE
+ */
+
+struct NSSCMSMessageStr {
+ NSSCMSContentInfo contentInfo; /* "outer" cinfo */
+ /* --------- local; not part of encoding --------- */
+ PLArenaPool *poolp;
+ PRBool poolp_is_ours;
+ int refCount;
+ /* properties of the "inner" data */
+ SECAlgorithmID **detached_digestalgs;
+ SECItem **detached_digests;
+ void *pwfn_arg;
+ NSSCMSGetDecryptKeyCallback decrypt_key_cb;
+ void *decrypt_key_cb_arg;
+};
+
+/* ============================================================================
+ * GENERIC WRAPPER
+ *
+ * used for user defined types.
+ */
+struct NSSCMSGenericWrapperDataStr {
+ NSSCMSContentInfo contentInfo;
+ /* ---- local; not part of encoding ------ */
+ NSSCMSMessage *cmsg;
+ /* wrapperspecific data starts here */
+};
+
+/* =============================================================================
+ * SIGNEDDATA
+ */
+
+struct NSSCMSSignedDataStr {
+ SECItem version;
+ SECAlgorithmID **digestAlgorithms;
+ NSSCMSContentInfo contentInfo;
+ SECItem **rawCerts;
+ CERTSignedCrl **crls;
+ NSSCMSSignerInfo **signerInfos;
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer to message */
+ SECItem **digests;
+ CERTCertificate **certs;
+ CERTCertificateList **certLists;
+ CERTCertificate **tempCerts; /* temporary certs, needed
+ * for example for signature
+ * verification */
+};
+#define NSS_CMS_SIGNED_DATA_VERSION_BASIC 1 /* what we *create* */
+#define NSS_CMS_SIGNED_DATA_VERSION_EXT 3 /* what we *create* */
+
+typedef enum {
+ NSSCMSVS_Unverified = 0,
+ NSSCMSVS_GoodSignature = 1,
+ NSSCMSVS_BadSignature = 2,
+ NSSCMSVS_DigestMismatch = 3,
+ NSSCMSVS_SigningCertNotFound = 4,
+ NSSCMSVS_SigningCertNotTrusted = 5,
+ NSSCMSVS_SignatureAlgorithmUnknown = 6,
+ NSSCMSVS_SignatureAlgorithmUnsupported = 7,
+ NSSCMSVS_MalformedSignature = 8,
+ NSSCMSVS_ProcessingError = 9
+} NSSCMSVerificationStatus;
+
+typedef enum {
+ NSSCMSSignerID_IssuerSN = 0,
+ NSSCMSSignerID_SubjectKeyID = 1
+} NSSCMSSignerIDSelector;
+
+struct NSSCMSSignerIdentifierStr {
+ NSSCMSSignerIDSelector identifierType;
+ union {
+ CERTIssuerAndSN *issuerAndSN;
+ SECItem *subjectKeyID;
+ } id;
+};
+
+struct NSSCMSSignerInfoStr {
+ SECItem version;
+ NSSCMSSignerIdentifier signerIdentifier;
+ SECAlgorithmID digestAlg;
+ NSSCMSAttribute **authAttr;
+ SECAlgorithmID digestEncAlg;
+ SECItem encDigest;
+ NSSCMSAttribute **unAuthAttr;
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer to message */
+ CERTCertificate *cert;
+ CERTCertificateList *certList;
+ PRTime signingTime;
+ NSSCMSVerificationStatus verificationStatus;
+ SECKEYPrivateKey *signingKey; /* Used if we're using subjKeyID*/
+ SECKEYPublicKey *pubKey;
+};
+#define NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN 1 /* what we *create* */
+#define NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY 3 /* what we *create* */
+
+typedef enum {
+ NSSCMSCM_None = 0,
+ NSSCMSCM_CertOnly = 1,
+ NSSCMSCM_CertChain = 2,
+ NSSCMSCM_CertChainWithRoot = 3
+} NSSCMSCertChainMode;
+
+/* =============================================================================
+ * ENVELOPED DATA
+ */
+struct NSSCMSEnvelopedDataStr {
+ SECItem version;
+ NSSCMSOriginatorInfo *originatorInfo; /* optional */
+ NSSCMSRecipientInfo **recipientInfos;
+ NSSCMSContentInfo contentInfo;
+ NSSCMSAttribute **unprotectedAttr;
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer to message */
+};
+#define NSS_CMS_ENVELOPED_DATA_VERSION_REG 0 /* what we *create* */
+#define NSS_CMS_ENVELOPED_DATA_VERSION_ADV 2 /* what we *create* */
+
+struct NSSCMSOriginatorInfoStr {
+ SECItem **rawCerts;
+ CERTSignedCrl **crls;
+ /* --------- local; not part of encoding --------- */
+ CERTCertificate **certs;
+};
+
+/* -----------------------------------------------------------------------------
+ * key transport recipient info
+ */
+typedef enum {
+ NSSCMSRecipientID_IssuerSN = 0,
+ NSSCMSRecipientID_SubjectKeyID = 1,
+ NSSCMSRecipientID_BrandNew = 2
+} NSSCMSRecipientIDSelector;
+
+struct NSSCMSRecipientIdentifierStr {
+ NSSCMSRecipientIDSelector identifierType;
+ union {
+ CERTIssuerAndSN *issuerAndSN;
+ SECItem *subjectKeyID;
+ } id;
+};
+typedef struct NSSCMSRecipientIdentifierStr NSSCMSRecipientIdentifier;
+
+struct NSSCMSKeyTransRecipientInfoStr {
+ SECItem version;
+ NSSCMSRecipientIdentifier recipientIdentifier;
+ SECAlgorithmID keyEncAlg;
+ SECItem encKey;
+};
+typedef struct NSSCMSKeyTransRecipientInfoStr NSSCMSKeyTransRecipientInfo;
+
+/*
+ * View comments before NSSCMSRecipientInfoStr for purpose of this
+ * structure.
+ */
+struct NSSCMSKeyTransRecipientInfoExStr {
+ NSSCMSKeyTransRecipientInfo recipientInfo;
+ int version; /* version of this structure (0) */
+ SECKEYPublicKey *pubKey;
+};
+
+typedef struct NSSCMSKeyTransRecipientInfoExStr NSSCMSKeyTransRecipientInfoEx;
+
+#define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN 0 /* what we *create* */
+#define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY 2 /* what we *create* */
+
+/* -----------------------------------------------------------------------------
+ * key agreement recipient info
+ */
+struct NSSCMSOriginatorPublicKeyStr {
+ SECAlgorithmID algorithmIdentifier;
+ SECItem publicKey; /* bit string! */
+};
+typedef struct NSSCMSOriginatorPublicKeyStr NSSCMSOriginatorPublicKey;
+
+typedef enum {
+ NSSCMSOriginatorIDOrKey_IssuerSN = 0,
+ NSSCMSOriginatorIDOrKey_SubjectKeyID = 1,
+ NSSCMSOriginatorIDOrKey_OriginatorPublicKey = 2
+} NSSCMSOriginatorIDOrKeySelector;
+
+struct NSSCMSOriginatorIdentifierOrKeyStr {
+ NSSCMSOriginatorIDOrKeySelector identifierType;
+ union {
+ CERTIssuerAndSN *issuerAndSN; /* static-static */
+ SECItem *subjectKeyID; /* static-static */
+ NSSCMSOriginatorPublicKey originatorPublicKey; /* ephemeral-static */
+ } id;
+};
+typedef struct NSSCMSOriginatorIdentifierOrKeyStr NSSCMSOriginatorIdentifierOrKey;
+
+struct NSSCMSRecipientKeyIdentifierStr {
+ SECItem *subjectKeyIdentifier;
+ SECItem *date; /* optional */
+ SECItem *other; /* optional */
+};
+typedef struct NSSCMSRecipientKeyIdentifierStr NSSCMSRecipientKeyIdentifier;
+
+typedef enum {
+ NSSCMSKeyAgreeRecipientID_IssuerSN = 0,
+ NSSCMSKeyAgreeRecipientID_RKeyID = 1
+} NSSCMSKeyAgreeRecipientIDSelector;
+
+struct NSSCMSKeyAgreeRecipientIdentifierStr {
+ NSSCMSKeyAgreeRecipientIDSelector identifierType;
+ union {
+ CERTIssuerAndSN *issuerAndSN;
+ NSSCMSRecipientKeyIdentifier recipientKeyIdentifier;
+ } id;
+};
+typedef struct NSSCMSKeyAgreeRecipientIdentifierStr NSSCMSKeyAgreeRecipientIdentifier;
+
+struct NSSCMSRecipientEncryptedKeyStr {
+ NSSCMSKeyAgreeRecipientIdentifier recipientIdentifier;
+ SECItem encKey;
+};
+typedef struct NSSCMSRecipientEncryptedKeyStr NSSCMSRecipientEncryptedKey;
+
+struct NSSCMSKeyAgreeRecipientInfoStr {
+ SECItem version;
+ NSSCMSOriginatorIdentifierOrKey originatorIdentifierOrKey;
+ SECItem *ukm; /* optional */
+ SECAlgorithmID keyEncAlg;
+ NSSCMSRecipientEncryptedKey **recipientEncryptedKeys;
+};
+typedef struct NSSCMSKeyAgreeRecipientInfoStr NSSCMSKeyAgreeRecipientInfo;
+
+#define NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION 3 /* what we *create* */
+
+/* -----------------------------------------------------------------------------
+ * KEK recipient info
+ */
+struct NSSCMSKEKIdentifierStr {
+ SECItem keyIdentifier;
+ SECItem *date; /* optional */
+ SECItem *other; /* optional */
+};
+typedef struct NSSCMSKEKIdentifierStr NSSCMSKEKIdentifier;
+
+struct NSSCMSKEKRecipientInfoStr {
+ SECItem version;
+ NSSCMSKEKIdentifier kekIdentifier;
+ SECAlgorithmID keyEncAlg;
+ SECItem encKey;
+};
+typedef struct NSSCMSKEKRecipientInfoStr NSSCMSKEKRecipientInfo;
+
+#define NSS_CMS_KEK_RECIPIENT_INFO_VERSION 4 /* what we *create* */
+
+/* -----------------------------------------------------------------------------
+ * recipient info
+ */
+
+typedef enum {
+ NSSCMSRecipientInfoID_KeyTrans = 0,
+ NSSCMSRecipientInfoID_KeyAgree = 1,
+ NSSCMSRecipientInfoID_KEK = 2
+} NSSCMSRecipientInfoIDSelector;
+
+/*
+ * In order to preserve backwards binary compatibility when implementing
+ * creation of Recipient Info's that uses subjectKeyID in the
+ * keyTransRecipientInfo we need to stash a public key pointer in this
+ * structure somewhere. We figured out that NSSCMSKeyTransRecipientInfo
+ * is the smallest member of the ri union. We're in luck since that's
+ * the very structure that would need to use the public key. So we created
+ * a new structure NSSCMSKeyTransRecipientInfoEx which has a member
+ * NSSCMSKeyTransRecipientInfo as the first member followed by a version
+ * and a public key pointer. This way we can keep backwards compatibility
+ * without changing the size of this structure.
+ *
+ * BTW, size of structure:
+ * NSSCMSKeyTransRecipientInfo: 9 ints, 4 pointers
+ * NSSCMSKeyAgreeRecipientInfo: 12 ints, 8 pointers
+ * NSSCMSKEKRecipientInfo: 10 ints, 7 pointers
+ *
+ * The new structure:
+ * NSSCMSKeyTransRecipientInfoEx: sizeof(NSSCMSKeyTransRecipientInfo) +
+ * 1 int, 1 pointer
+ */
+
+struct NSSCMSRecipientInfoStr {
+ NSSCMSRecipientInfoIDSelector recipientInfoType;
+ union {
+ NSSCMSKeyTransRecipientInfo keyTransRecipientInfo;
+ NSSCMSKeyAgreeRecipientInfo keyAgreeRecipientInfo;
+ NSSCMSKEKRecipientInfo kekRecipientInfo;
+ NSSCMSKeyTransRecipientInfoEx keyTransRecipientInfoEx;
+ } ri;
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer to message */
+ CERTCertificate *cert; /* recipient's certificate */
+};
+
+/* =============================================================================
+ * DIGESTED DATA
+ */
+struct NSSCMSDigestedDataStr {
+ SECItem version;
+ SECAlgorithmID digestAlg;
+ NSSCMSContentInfo contentInfo;
+ SECItem digest;
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer */
+ SECItem cdigest; /* calculated digest */
+};
+#define NSS_CMS_DIGESTED_DATA_VERSION_DATA 0 /* what we *create* */
+#define NSS_CMS_DIGESTED_DATA_VERSION_ENCAP 2 /* what we *create* */
+
+/* =============================================================================
+ * ENCRYPTED DATA
+ */
+struct NSSCMSEncryptedDataStr {
+ SECItem version;
+ NSSCMSContentInfo contentInfo;
+ NSSCMSAttribute **unprotectedAttr; /* optional */
+ /* --------- local; not part of encoding --------- */
+ NSSCMSMessage *cmsg; /* back pointer */
+};
+#define NSS_CMS_ENCRYPTED_DATA_VERSION 0 /* what we *create* */
+#define NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR 2 /* what we *create* */
+
+/*
+ * *****************************************************************************
+ * *****************************************************************************
+ * *****************************************************************************
+ */
+
+/*
+ * See comment above about this type not really belonging to CMS.
+ */
+struct NSSCMSAttributeStr {
+ /* The following fields make up an encoded Attribute: */
+ SECItem type;
+ SECItem **values; /* data may or may not be encoded */
+ /* The following fields are not part of an encoded Attribute: */
+ SECOidData *typeTag;
+ PRBool encoded; /* when true, values are encoded */
+};
+
+#endif /* _CMST_H_ */
diff --git a/security/nss/lib/smime/cmsudf.c b/security/nss/lib/smime/cmsudf.c
new file mode 100644
index 0000000000..5c8a81e6df
--- /dev/null
+++ b/security/nss/lib/smime/cmsudf.c
@@ -0,0 +1,440 @@
+/* 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 User Define Types
+ */
+
+#include "cmslocal.h"
+
+#include "prinit.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "nss.h"
+
+typedef struct nsscmstypeInfoStr nsscmstypeInfo;
+struct nsscmstypeInfoStr {
+ SECOidTag type;
+ SEC_ASN1Template *template;
+ size_t size;
+ PRBool isData;
+ NSSCMSGenericWrapperDataDestroy destroy;
+ NSSCMSGenericWrapperDataCallback decode_before;
+ NSSCMSGenericWrapperDataCallback decode_after;
+ NSSCMSGenericWrapperDataCallback decode_end;
+ NSSCMSGenericWrapperDataCallback encode_start;
+ NSSCMSGenericWrapperDataCallback encode_before;
+ NSSCMSGenericWrapperDataCallback encode_after;
+};
+
+/* make sure the global tables are only initialized once */
+static PRCallOnceType nsscmstypeOnce;
+static PRCallOnceType nsscmstypeClearOnce;
+/* lock for adding a new entry */
+static PRLock *nsscmstypeAddLock;
+/* lock for the hash table */
+static PRLock *nsscmstypeHashLock;
+/* the hash table itself */
+static PLHashTable *nsscmstypeHash;
+/* arena to hold all the hash table data */
+static PLArenaPool *nsscmstypeArena;
+
+/*
+ * clean up our global tables
+ */
+SECStatus
+nss_cmstype_shutdown(void *appData, void *reserved)
+{
+ if (nsscmstypeHashLock) {
+ PR_Lock(nsscmstypeHashLock);
+ }
+ if (nsscmstypeHash) {
+ PL_HashTableDestroy(nsscmstypeHash);
+ nsscmstypeHash = NULL;
+ }
+ if (nsscmstypeArena) {
+ PORT_FreeArena(nsscmstypeArena, PR_FALSE);
+ nsscmstypeArena = NULL;
+ }
+ if (nsscmstypeAddLock) {
+ PR_DestroyLock(nsscmstypeAddLock);
+ }
+ if (nsscmstypeHashLock) {
+ PRLock *oldLock = nsscmstypeHashLock;
+ nsscmstypeHashLock = NULL;
+ PR_Unlock(oldLock);
+ PR_DestroyLock(oldLock);
+ }
+
+ /* don't clear out the PR_ONCE data if we failed our inital call */
+ if (appData == NULL) {
+ nsscmstypeOnce = nsscmstypeClearOnce;
+ }
+ return SECSuccess;
+}
+
+static PLHashNumber
+nss_cmstype_hash_key(const void *key)
+{
+ return (PLHashNumber)((char *)key - (char *)NULL);
+}
+
+static PRIntn
+nss_cmstype_compare_keys(const void *v1, const void *v2)
+{
+ PLHashNumber value1 = nss_cmstype_hash_key(v1);
+ PLHashNumber value2 = nss_cmstype_hash_key(v2);
+
+ return (value1 == value2);
+}
+
+/*
+ * initialize our hash tables, called once on the first attemat to register
+ * a new SMIME type.
+ */
+static PRStatus
+nss_cmstype_init(void)
+{
+ SECStatus rv;
+
+ nsscmstypeHashLock = PR_NewLock();
+ if (nsscmstypeHashLock == NULL) {
+ return PR_FAILURE;
+ }
+ nsscmstypeAddLock = PR_NewLock();
+ if (nsscmstypeHashLock == NULL) {
+ goto fail;
+ }
+ nsscmstypeHash = PL_NewHashTable(64, nss_cmstype_hash_key,
+ nss_cmstype_compare_keys,
+ PL_CompareValues, NULL, NULL);
+ if (nsscmstypeHash == NULL) {
+ goto fail;
+ }
+ nsscmstypeArena = PORT_NewArena(2048);
+ if (nsscmstypeArena == NULL) {
+ goto fail;
+ }
+ rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL);
+ if (rv != SECSuccess) {
+ goto fail;
+ }
+ return PR_SUCCESS;
+
+fail:
+ nss_cmstype_shutdown(&nsscmstypeOnce, NULL);
+ return PR_FAILURE;
+}
+
+/*
+ * look up and registered SIME type
+ */
+static const nsscmstypeInfo *
+nss_cmstype_lookup(SECOidTag type)
+{
+ nsscmstypeInfo *typeInfo = NULL;
+ ;
+ if (!nsscmstypeHash) {
+ return NULL;
+ }
+ PR_Lock(nsscmstypeHashLock);
+ if (nsscmstypeHash) {
+ typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type);
+ }
+ PR_Unlock(nsscmstypeHashLock);
+ return typeInfo;
+}
+
+/*
+ * add a new type to the SMIME type table
+ */
+static SECStatus
+nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo)
+{
+ PLHashEntry *entry;
+
+ if (!nsscmstypeHash) {
+ /* assert? this shouldn't happen */
+ return SECFailure;
+ }
+ PR_Lock(nsscmstypeHashLock);
+ /* this is really paranoia. If we really are racing nsscmstypeHash, we'll
+ * also be racing nsscmstypeHashLock... */
+ if (!nsscmstypeHash) {
+ PR_Unlock(nsscmstypeHashLock);
+ return SECFailure;
+ }
+ entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo);
+ PR_Unlock(nsscmstypeHashLock);
+ return entry ? SECSuccess : SECFailure;
+}
+
+/* helper functions to manage new content types
+ */
+
+PRBool
+NSS_CMSType_IsWrapper(SECOidTag type)
+{
+ const nsscmstypeInfo *typeInfo = NULL;
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ return PR_TRUE;
+ default:
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo && !typeInfo->isData) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+PRBool
+NSS_CMSType_IsData(SECOidTag type)
+{
+ const nsscmstypeInfo *typeInfo = NULL;
+
+ switch (type) {
+ case SEC_OID_PKCS7_DATA:
+ return PR_TRUE;
+ default:
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo && typeInfo->isData) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+const SEC_ASN1Template *
+NSS_CMSType_GetTemplate(SECOidTag type)
+{
+ const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
+
+ if (typeInfo && typeInfo->template) {
+ return typeInfo->template;
+ }
+ return SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
+}
+
+size_t
+NSS_CMSType_GetContentSize(SECOidTag type)
+{
+ const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
+
+ if (typeInfo) {
+ return typeInfo->size;
+ }
+ return sizeof(SECItem *);
+}
+
+void
+NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type);
+
+ if (typeInfo && (typeInfo->destroy) && (gd != NULL)) {
+ (*typeInfo->destroy)(gd);
+ }
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->decode_before) {
+ return (*typeInfo->decode_before)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->decode_after) {
+ return (*typeInfo->decode_after)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->decode_end) {
+ return (*typeInfo->decode_end)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->encode_start) {
+ return (*typeInfo->encode_start)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->encode_before) {
+ return (*typeInfo->encode_before)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type,
+ NSSCMSGenericWrapperData *gd)
+{
+ const nsscmstypeInfo *typeInfo;
+
+ /* short cut common case */
+ if (type == SEC_OID_PKCS7_DATA) {
+ return SECSuccess;
+ }
+
+ typeInfo = nss_cmstype_lookup(type);
+ if (typeInfo) {
+ if (typeInfo->encode_after) {
+ return (*typeInfo->encode_after)(gd);
+ }
+ /* decoder ops optional for data tags */
+ if (typeInfo->isData) {
+ return SECSuccess;
+ }
+ }
+ /* expected a function, but none existed */
+ return SECFailure;
+}
+
+SECStatus
+NSS_CMSType_RegisterContentType(SECOidTag type,
+ SEC_ASN1Template *asn1Template, size_t size,
+ NSSCMSGenericWrapperDataDestroy destroy,
+ NSSCMSGenericWrapperDataCallback decode_before,
+ NSSCMSGenericWrapperDataCallback decode_after,
+ NSSCMSGenericWrapperDataCallback decode_end,
+ NSSCMSGenericWrapperDataCallback encode_start,
+ NSSCMSGenericWrapperDataCallback encode_before,
+ NSSCMSGenericWrapperDataCallback encode_after,
+ PRBool isData)
+{
+ PRStatus rc;
+ SECStatus rv;
+ nsscmstypeInfo *typeInfo;
+ const nsscmstypeInfo *exists;
+
+ rc = PR_CallOnce(&nsscmstypeOnce, nss_cmstype_init);
+ if (rc == PR_FAILURE) {
+ return SECFailure;
+ }
+ PR_Lock(nsscmstypeAddLock);
+ exists = nss_cmstype_lookup(type);
+ if (exists) {
+ PR_Unlock(nsscmstypeAddLock);
+ /* already added */
+ return SECSuccess;
+ }
+ typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo);
+ typeInfo->type = type;
+ typeInfo->size = size;
+ typeInfo->isData = isData;
+ typeInfo->template = asn1Template;
+ typeInfo->destroy = destroy;
+ typeInfo->decode_before = decode_before;
+ typeInfo->decode_after = decode_after;
+ typeInfo->decode_end = decode_end;
+ typeInfo->encode_start = encode_start;
+ typeInfo->encode_before = encode_before;
+ typeInfo->encode_after = encode_after;
+ rv = nss_cmstype_add(type, typeInfo);
+ PR_Unlock(nsscmstypeAddLock);
+ return rv;
+}
diff --git a/security/nss/lib/smime/cmsutil.c b/security/nss/lib/smime/cmsutil.c
new file mode 100644
index 0000000000..844fd22a93
--- /dev/null
+++ b/security/nss/lib/smime/cmsutil.c
@@ -0,0 +1,371 @@
+/* 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 miscellaneous utility functions.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "secerr.h"
+#include "sechash.h"
+
+/*
+ * NSS_CMSArray_SortByDER - sort array of objects by objects' DER encoding
+ *
+ * make sure that the order of the objects guarantees valid DER (which must be
+ * in lexigraphically ascending order for a SET OF); if reordering is necessary it
+ * will be done in place (in objs).
+ */
+SECStatus
+NSS_CMSArray_SortByDER(void **objs, const SEC_ASN1Template *objtemplate, void **objs2)
+{
+ PLArenaPool *poolp;
+ int num_objs;
+ SECItem **enc_objs;
+ SECStatus rv = SECFailure;
+ int i;
+
+ if (objs == NULL) /* already sorted */
+ return SECSuccess;
+
+ num_objs = NSS_CMSArray_Count((void **)objs);
+ if (num_objs == 0 || num_objs == 1) /* already sorted. */
+ return SECSuccess;
+
+ poolp = PORT_NewArena(1024); /* arena for temporaries */
+ if (poolp == NULL)
+ return SECFailure; /* no memory; nothing we can do... */
+
+ /*
+ * Allocate arrays to hold the individual encodings which we will use
+ * for comparisons and the reordered attributes as they are sorted.
+ */
+ enc_objs = (SECItem **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SECItem *));
+ if (enc_objs == NULL)
+ goto loser;
+
+ /* DER encode each individual object. */
+ for (i = 0; i < num_objs; i++) {
+ enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate);
+ if (enc_objs[i] == NULL)
+ goto loser;
+ }
+ enc_objs[num_objs] = NULL;
+
+ /* now compare and sort objs by the order of enc_objs */
+ NSS_CMSArray_Sort((void **)enc_objs, NSS_CMSUtil_DERCompare, objs, objs2);
+
+ rv = SECSuccess;
+
+loser:
+ PORT_FreeArena(poolp, PR_FALSE);
+ return rv;
+}
+
+/*
+ * NSS_CMSUtil_DERCompare - for use with NSS_CMSArray_Sort to
+ * sort arrays of SECItems containing DER
+ */
+int
+NSS_CMSUtil_DERCompare(void *a, void *b)
+{
+ SECItem *der1 = (SECItem *)a;
+ SECItem *der2 = (SECItem *)b;
+ unsigned int j;
+
+ /*
+ * Find the lowest (lexigraphically) encoding. One that is
+ * shorter than all the rest is known to be "less" because each
+ * attribute is of the same type (a SEQUENCE) and so thus the
+ * first octet of each is the same, and the second octet is
+ * the length (or the length of the length with the high bit
+ * set, followed by the length, which also works out to always
+ * order the shorter first). Two (or more) that have the
+ * same length need to be compared byte by byte until a mismatch
+ * is found.
+ */
+ if (der1->len != der2->len)
+ return (der1->len < der2->len) ? -1 : 1;
+
+ for (j = 0; j < der1->len; j++) {
+ if (der1->data[j] == der2->data[j])
+ continue;
+ return (der1->data[j] < der2->data[j]) ? -1 : 1;
+ }
+ return 0;
+}
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgID - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algid - algorithmid of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+int
+NSS_CMSAlgArray_GetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid)
+{
+ int i;
+
+ if (algorithmArray == NULL || algorithmArray[0] == NULL)
+ return -1;
+
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual)
+ break; /* bingo */
+ }
+
+ if (algorithmArray[i] == NULL)
+ return -1; /* not found */
+
+ return i;
+}
+
+/*
+ * NSS_CMSAlgArray_GetIndexByAlgTag - find a specific algorithm in an array of
+ * algorithms.
+ *
+ * algorithmArray - array of algorithm IDs
+ * algtag - algorithm tag of algorithm to pick
+ *
+ * Returns:
+ * An integer containing the index of the algorithm in the array or -1 if
+ * algorithm was not found.
+ */
+int
+NSS_CMSAlgArray_GetIndexByAlgTag(SECAlgorithmID **algorithmArray,
+ SECOidTag algtag)
+{
+ SECOidData *algid;
+ int i = -1;
+
+ if (algorithmArray == NULL || algorithmArray[0] == NULL)
+ return i;
+
+#ifdef ORDER_N_SQUARED
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ algid = SECOID_FindOID(&(algorithmArray[i]->algorithm));
+ if (algid->offset == algtag)
+ break; /* bingo */
+ }
+#else
+ algid = SECOID_FindOIDByTag(algtag);
+ if (!algid)
+ return i;
+ for (i = 0; algorithmArray[i] != NULL; i++) {
+ if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid))
+ break; /* bingo */
+ }
+#endif
+
+ if (algorithmArray[i] == NULL)
+ return -1; /* not found */
+
+ return i;
+}
+
+/*
+ * Map a sign algorithm to a digest algorithm.
+ * This is used to handle incorrectly formatted packages sent to us
+ * from Windows 2003.
+ */
+SECOidTag
+NSS_CMSUtil_MapSignAlgs(SECOidTag signAlg)
+{
+ switch (signAlg) {
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ return SEC_OID_MD2;
+ break;
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ return SEC_OID_MD5;
+ break;
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
+ case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ return SEC_OID_SHA1;
+ break;
+ case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
+ return SEC_OID_SHA256;
+ break;
+ case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
+ return SEC_OID_SHA384;
+ break;
+ case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+ case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
+ return SEC_OID_SHA512;
+ break;
+ default:
+ break;
+ }
+ /* not one of the algtags incorrectly sent to us*/
+ return signAlg;
+}
+
+const SECHashObject *
+NSS_CMSUtil_GetHashObjByAlgID(SECAlgorithmID *algid)
+{
+ SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm));
+ const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
+
+ return digobj;
+}
+
+const SEC_ASN1Template *
+NSS_CMSUtil_GetTemplateByTypeTag(SECOidTag type)
+{
+ const SEC_ASN1Template *template;
+ extern const SEC_ASN1Template NSSCMSSignedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSEnvelopedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSEncryptedDataTemplate[];
+ extern const SEC_ASN1Template NSSCMSDigestedDataTemplate[];
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ template = NSSCMSSignedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ template = NSSCMSEnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ template = NSSCMSEncryptedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ template = NSSCMSDigestedDataTemplate;
+ break;
+ default:
+ template = NSS_CMSType_GetTemplate(type);
+ break;
+ }
+ return template;
+}
+
+size_t
+NSS_CMSUtil_GetSizeByTypeTag(SECOidTag type)
+{
+ size_t size;
+
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ size = sizeof(NSSCMSSignedData);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ size = sizeof(NSSCMSEnvelopedData);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ size = sizeof(NSSCMSEncryptedData);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ size = sizeof(NSSCMSDigestedData);
+ break;
+ default:
+ size = NSS_CMSType_GetContentSize(type);
+ break;
+ }
+ return size;
+}
+
+NSSCMSContentInfo *
+NSS_CMSContent_GetContentInfo(void *msg, SECOidTag type)
+{
+ NSSCMSContent c;
+ NSSCMSContentInfo *cinfo = NULL;
+
+ if (!msg)
+ return cinfo;
+ c.pointer = msg;
+ switch (type) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ cinfo = &(c.signedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ cinfo = &(c.envelopedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ cinfo = &(c.encryptedData->contentInfo);
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ cinfo = &(c.digestedData->contentInfo);
+ break;
+ default:
+ cinfo = NULL;
+ if (NSS_CMSType_IsWrapper(type)) {
+ cinfo = &(c.genericData->contentInfo);
+ }
+ }
+ /* We are using a union as a form of 'safe casting'. This
+ * syntax confuses cppcheck, so tell it it's OK (and any human
+ * who happens along to verify any other scanner warnings) */
+ /* cppcheck-suppress returnDanglingLifetime */
+ return cinfo;
+}
+
+const char *
+NSS_CMSUtil_VerificationStatusToString(NSSCMSVerificationStatus vs)
+{
+ switch (vs) {
+ case NSSCMSVS_Unverified:
+ return "Unverified";
+ case NSSCMSVS_GoodSignature:
+ return "GoodSignature";
+ case NSSCMSVS_BadSignature:
+ return "BadSignature";
+ case NSSCMSVS_DigestMismatch:
+ return "DigestMismatch";
+ case NSSCMSVS_SigningCertNotFound:
+ return "SigningCertNotFound";
+ case NSSCMSVS_SigningCertNotTrusted:
+ return "SigningCertNotTrusted";
+ case NSSCMSVS_SignatureAlgorithmUnknown:
+ return "SignatureAlgorithmUnknown";
+ case NSSCMSVS_SignatureAlgorithmUnsupported:
+ return "SignatureAlgorithmUnsupported";
+ case NSSCMSVS_MalformedSignature:
+ return "MalformedSignature";
+ case NSSCMSVS_ProcessingError:
+ return "ProcessingError";
+ default:
+ return "Unknown";
+ }
+}
+
+SECStatus
+NSS_CMSDEREncode(NSSCMSMessage *cmsg, SECItem *input, SECItem *derOut,
+ PLArenaPool *arena)
+{
+ NSSCMSEncoderContext *ecx;
+ SECStatus rv = SECSuccess;
+ if (!cmsg || !derOut || !arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ ecx = NSS_CMSEncoder_Start(cmsg, 0, 0, derOut, arena, 0, 0, 0, 0, 0, 0);
+ if (!ecx) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (input) {
+ rv = NSS_CMSEncoder_Update(ecx, (const char *)input->data, input->len);
+ if (rv) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ }
+ }
+ rv |= NSS_CMSEncoder_Finish(ecx);
+ if (rv) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+ return rv;
+}
diff --git a/security/nss/lib/smime/config.mk b/security/nss/lib/smime/config.mk
new file mode 100644
index 0000000000..76aab75442
--- /dev/null
+++ b/security/nss/lib/smime/config.mk
@@ -0,0 +1,56 @@
+#
+# 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/.
+
+RELEASE_LIBS = $(TARGETS)
+
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+
+ifdef NS_USE_GCC
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnss3 \
+ -L$(NSSUTIL_LIB_DIR) \
+ -lnssutil3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+else # ! NS_USE_GCC
+EXTRA_SHARED_LIBS += \
+ $(DIST)/lib/nss3.lib \
+ $(DIST)/lib/nssutil3.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \
+ $(NULL)
+endif # NS_USE_GCC
+
+else
+
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnss3 \
+ -L$(NSSUTIL_LIB_DIR) \
+ -lnssutil3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+
+endif
+
+
+SHARED_LIBRARY_LIBS = \
+ $(DIST)/lib/$(LIB_PREFIX)pkcs12.$(LIB_SUFFIX) \
+ $(DIST)/lib/$(LIB_PREFIX)pkcs7.$(LIB_SUFFIX) \
+ $(NULL)
+
+SHARED_LIBRARY_DIRS = \
+ ../pkcs12 \
+ ../pkcs7 \
+ $(NULL)
+
diff --git a/security/nss/lib/smime/exports.gyp b/security/nss/lib/smime/exports.gyp
new file mode 100644
index 0000000000..3865bb4dac
--- /dev/null
+++ b/security/nss/lib/smime/exports.gyp
@@ -0,0 +1,34 @@
+# 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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'lib_smime_exports',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'files': [
+ 'cms.h',
+ 'cmsreclist.h',
+ 'cmst.h',
+ 'smime.h'
+ ],
+ 'destination': '<(nss_public_dist_dir)/<(module)'
+ },
+ {
+ 'files': [
+ 'cmslocal.h'
+ ],
+ 'destination': '<(nss_private_dist_dir)/<(module)'
+ }
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/smime/manifest.mn b/security/nss/lib/smime/manifest.mn
new file mode 100644
index 0000000000..f453d25949
--- /dev/null
+++ b/security/nss/lib/smime/manifest.mn
@@ -0,0 +1,51 @@
+#
+# 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/.
+
+CORE_DEPTH = ../..
+
+EXPORTS = \
+ cms.h \
+ cmst.h \
+ smime.h \
+ cmsreclist.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ cmslocal.h \
+ $(NULL)
+
+MODULE = nss
+
+CSRCS = \
+ cmsarray.c \
+ cmsasn1.c \
+ cmsattr.c \
+ cmscinfo.c \
+ cmscipher.c \
+ cmsdecode.c \
+ cmsdigdata.c \
+ cmsdigest.c \
+ cmsencdata.c \
+ cmsencode.c \
+ cmsenvdata.c \
+ cmsmessage.c \
+ cmspubkey.c \
+ cmsrecinfo.c \
+ cmsreclist.c \
+ cmssigdata.c \
+ cmssiginfo.c \
+ cmsudf.c \
+ cmsutil.c \
+ smimemessage.c \
+ smimeutil.c \
+ smimever.c \
+ $(NULL)
+
+LIBRARY_NAME = smime
+LIBRARY_VERSION = 3
+MAPFILE = $(OBJDIR)/$(LIBRARY_NAME).def
+
+# This part of the code, including all sub-dirs, can be optimized for size
+export ALLOW_OPT_CODE_SIZE = 1
diff --git a/security/nss/lib/smime/smime.def b/security/nss/lib/smime/smime.def
new file mode 100644
index 0000000000..ba9d09d8cb
--- /dev/null
+++ b/security/nss/lib/smime/smime.def
@@ -0,0 +1,293 @@
+;+#
+;+# 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/.
+;+#
+;+# OK, this file is meant to support SUN, LINUX, AIX and WINDOWS
+;+# 1. For all unix platforms, the string ";-" means "remove this line"
+;+# 2. For all unix platforms, the string " DATA " will be removed from any
+;+# line on which it occurs.
+;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX.
+;+# On AIX, lines containing ";+" will be removed.
+;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed.
+;+# 5. For all unix platforms, after the above processing has taken place,
+;+# all characters after the first ";" on the line will be removed.
+;+# And for AIX, the first ";" will also be removed.
+;+# This file is passed directly to windows. Since ';' is a comment, all UNIX
+;+# directives are hidden behind ";", ";+", and ";-"
+;+
+;+NSS_3.2 { # NSS 3.2 release
+;+ global:
+LIBRARY smime3 ;-
+EXPORTS ;-
+NSS_CMSContentInfo_GetBulkKey;
+NSS_CMSContentInfo_GetBulkKeySize;
+NSS_CMSContentInfo_GetContent;
+NSS_CMSContentInfo_GetContentEncAlgTag;
+NSS_CMSContentInfo_GetContentTypeTag;
+NSS_CMSContentInfo_SetBulkKey;
+NSS_CMSContentInfo_SetContent;
+NSS_CMSContentInfo_SetContentEncAlg;
+NSS_CMSContentInfo_SetContent_Data;
+NSS_CMSContentInfo_SetContent_DigestedData;
+NSS_CMSContentInfo_SetContent_EncryptedData;
+NSS_CMSContentInfo_SetContent_EnvelopedData;
+NSS_CMSContentInfo_SetContent_SignedData;
+NSS_CMSDEREncode;
+NSS_CMSDecoder_Cancel;
+NSS_CMSDecoder_Finish;
+NSS_CMSDecoder_Start;
+NSS_CMSDecoder_Update;
+NSS_CMSDigestContext_Cancel;
+NSS_CMSDigestContext_FinishMultiple;
+NSS_CMSDigestContext_FinishSingle;
+NSS_CMSDigestContext_StartMultiple;
+NSS_CMSDigestContext_StartSingle;
+NSS_CMSDigestContext_Update;
+NSS_CMSDigestedData_Create;
+NSS_CMSDigestedData_Destroy;
+NSS_CMSDigestedData_GetContentInfo;
+NSS_CMSEncoder_Cancel;
+NSS_CMSEncoder_Finish;
+NSS_CMSEncoder_Start;
+NSS_CMSEncoder_Update;
+NSS_CMSEncryptedData_Create;
+NSS_CMSEncryptedData_Destroy;
+NSS_CMSEncryptedData_GetContentInfo;
+NSS_CMSEnvelopedData_AddRecipient;
+NSS_CMSEnvelopedData_Create;
+NSS_CMSEnvelopedData_Destroy;
+NSS_CMSEnvelopedData_GetContentInfo;
+NSS_CMSMessage_ContentLevel;
+NSS_CMSMessage_ContentLevelCount;
+NSS_CMSMessage_Copy;
+NSS_CMSMessage_Create;
+NSS_CMSMessage_CreateFromDER;
+NSS_CMSMessage_Destroy;
+NSS_CMSMessage_GetContent;
+NSS_CMSMessage_GetContentInfo;
+NSS_CMSRecipientInfo_Create;
+NSS_CMSRecipientInfo_Destroy;
+NSS_CMSSignedData_AddCertChain;
+NSS_CMSSignedData_AddCertList;
+NSS_CMSSignedData_AddCertificate;
+NSS_CMSSignedData_AddDigest;
+NSS_CMSSignedData_AddSignerInfo;
+NSS_CMSSignedData_Create;
+NSS_CMSSignedData_CreateCertsOnly;
+NSS_CMSSignedData_Destroy;
+NSS_CMSSignedData_GetContentInfo;
+NSS_CMSSignedData_GetDigestAlgs;
+NSS_CMSSignedData_GetSignerInfo;
+NSS_CMSSignedData_HasDigests;
+NSS_CMSSignedData_ImportCerts;
+NSS_CMSSignedData_SetDigests;
+NSS_CMSSignedData_SignerInfoCount;
+NSS_CMSSignedData_VerifyCertsOnly;
+NSS_CMSSignedData_VerifySignerInfo;
+NSS_CMSSignerInfo_AddSMIMECaps;
+NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs;
+NSS_CMSSignerInfo_AddSigningTime;
+NSS_CMSSignerInfo_Create;
+NSS_CMSSignerInfo_Destroy;
+NSS_CMSSignerInfo_GetCertList;
+NSS_CMSSignerInfo_GetSignerCommonName;
+NSS_CMSSignerInfo_GetSignerEmailAddress;
+NSS_CMSSignerInfo_GetSigningCertificate;
+NSS_CMSSignerInfo_GetSigningTime;
+NSS_CMSSignerInfo_GetVerificationStatus;
+NSS_CMSSignerInfo_GetVersion;
+NSS_CMSSignerInfo_IncludeCerts;
+NSS_CMSUtil_VerificationStatusToString;
+NSS_SMIMEUtil_FindBulkAlgForRecipients;
+CERT_DecodeCertPackage;
+SEC_PKCS7AddRecipient;
+SEC_PKCS7AddSigningTime;
+SEC_PKCS7ContentType;
+SEC_PKCS7CreateData;
+SEC_PKCS7CreateEncryptedData;
+SEC_PKCS7CreateEnvelopedData;
+SEC_PKCS7CreateSignedData;
+SEC_PKCS7DecodeItem;
+SEC_PKCS7DecoderFinish;
+SEC_PKCS7DecoderStart;
+SEC_PKCS7DecoderUpdate;
+SEC_PKCS7DecryptContents;
+SEC_PKCS7DestroyContentInfo;
+SEC_PKCS7EncoderFinish;
+SEC_PKCS7EncoderStart;
+SEC_PKCS7EncoderUpdate;
+SEC_PKCS7GetCertificateList;
+SEC_PKCS7GetContent;
+SEC_PKCS7GetEncryptionAlgorithm;
+SEC_PKCS7IncludeCertChain;
+SEC_PKCS7IsContentEmpty;
+SEC_PKCS7VerifySignature;
+SEC_PKCS12AddCertAndKey;
+SEC_PKCS12AddPasswordIntegrity;
+SEC_PKCS12CreateExportContext;
+SEC_PKCS12CreatePasswordPrivSafe;
+SEC_PKCS12CreateUnencryptedSafe;
+SEC_PKCS12EnableCipher;
+SEC_PKCS12Encode;
+SEC_PKCS12DecoderImportBags;
+SEC_PKCS12DecoderFinish;
+SEC_PKCS12DecoderStart;
+SEC_PKCS12DecoderUpdate;
+SEC_PKCS12DecoderValidateBags;
+SEC_PKCS12DecoderVerify;
+SEC_PKCS12DestroyExportContext;
+SEC_PKCS12IsEncryptionAllowed;
+SEC_PKCS12SetPreferredCipher;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.2.1 { # NSS 3.2.1 release
+;+ global:
+NSSSMIME_VersionCheck;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.3 { # NSS 3.3 release
+;+ global:
+SEC_PKCS7AddCertificate;
+SEC_PKCS7CreateCertsOnly;
+SEC_PKCS7Encode;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.4 { # NSS 3.4 release
+;+ global:
+CERT_DecodeCertFromPackage;
+NSS_CMSMessage_IsSigned;
+NSS_CMSSignedData_SetDigestValue;
+NSS_SMIMESignerInfo_SaveSMIMEProfile;
+SEC_PKCS12DecoderGetCerts;
+SEC_PKCS7ContainsCertsOrCrls;
+SEC_PKCS7ContentIsEncrypted;
+SEC_PKCS7ContentIsSigned;
+SEC_PKCS7CopyContentInfo;
+SEC_PKCS7GetSignerCommonName;
+SEC_PKCS7GetSignerEmailAddress;
+SEC_PKCS7GetSigningTime;
+SEC_PKCS7SetContent;
+SEC_PKCS7VerifyDetachedSignature;
+SECMIME_DecryptionAllowed;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.4.1 { # NSS 3.4.1 release
+;+ global:
+NSS_CMSMessage_IsEncrypted;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.6 { # NSS 3.6 release
+;+ global:
+NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs;
+NSS_CMSSignerInfo_CreateWithSubjKeyID;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.7 { # NSS 3.7 release
+;+ global:
+NSS_CMSRecipientInfo_CreateWithSubjKeyID;
+NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.7.2 { # NSS 3.7.2 release
+;+ global:
+NSS_CMSRecipientInfo_WrapBulkKey;
+NSS_CMSRecipientInfo_UnwrapBulkKey;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.8 { # NSS 3.8 release
+;+ global:
+NSS_CMSRecipientInfo_CreateNew;
+NSS_CMSRecipientInfo_CreateFromDER;
+NSS_CMSRecipientInfo_Encode;
+NSS_CMSRecipientInfo_GetCertAndKey;
+SEC_PKCS12DecoderSetTargetTokenCAs;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.9 { # NSS 3.9 release
+;+ global:
+SEC_PKCS7DecoderAbort;
+SEC_PKCS7EncoderAbort;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.9.3 { # NSS 3.9.3 release
+;+ global:
+CERT_ConvertAndDecodeCertificate;
+SEC_PKCS7EncodeItem;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.10 { # NSS 3.10 release
+;+ global:
+SEC_PKCS12DecoderIterateInit;
+SEC_PKCS12DecoderIterateNext;
+SEC_PKCS12DecryptionAllowed;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.12.2 { # NSS 3.12.2 release
+;+ global:
+SEC_PKCS12AddCertOrChainAndKey;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.12.10 { # NSS 3.12.10 release
+;+ global:
+NSS_CMSType_RegisterContentType;
+NSS_CMSContentInfo_SetDontStream;
+NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs;
+;+#
+;+# Data objects
+;+#
+;+# Don't export these DATA symbols on Windows because they don't work right.
+;+# Use the SEC_ASN1_GET / SEC_ASN1_SUB / SEC_ASN1_XTRN macros to access them.
+;+#
+;+# See nssutil for other examples.
+;+#
+;;NSSCMSGenericWrapperDataTemplate DATA ;
+;;NSS_PointerToCMSGenericWrapperDataTemplate DATA ;
+NSS_Get_NSSCMSGenericWrapperDataTemplate;
+NSS_Get_NSS_PointerToCMSGenericWrapperDataTemplate;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.13 { # NSS 3.13 release
+;+ global:
+NSSSMIME_GetVersion;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.15 { # NSS 3.15 release
+;+ global:
+SEC_PKCS7VerifyDetachedSignatureAtTime;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.16 { # NSS 3.16 release
+;+ global:
+NSS_CMSSignerInfo_Verify;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.18 { # NSS 3.18 release
+;+ global:
+SEC_PKCS12DecoderRenameCertNicknames;
+;+ local:
+;+ *;
+;+};
+;+NSS_3.89 { # NSS 3.89 release
+;+ global:
+NSS_CMSSignerInfo_GetDigestAlgTag;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/smime/smime.gyp b/security/nss/lib/smime/smime.gyp
new file mode 100644
index 0000000000..fa74cd9468
--- /dev/null
+++ b/security/nss/lib/smime/smime.gyp
@@ -0,0 +1,80 @@
+# 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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'smime',
+ 'type': 'static_library',
+ 'sources': [
+ 'cmsarray.c',
+ 'cmsasn1.c',
+ 'cmsattr.c',
+ 'cmscinfo.c',
+ 'cmscipher.c',
+ 'cmsdecode.c',
+ 'cmsdigdata.c',
+ 'cmsdigest.c',
+ 'cmsencdata.c',
+ 'cmsencode.c',
+ 'cmsenvdata.c',
+ 'cmsmessage.c',
+ 'cmspubkey.c',
+ 'cmsrecinfo.c',
+ 'cmsreclist.c',
+ 'cmssigdata.c',
+ 'cmssiginfo.c',
+ 'cmsudf.c',
+ 'cmsutil.c',
+ 'smimemessage.c',
+ 'smimeutil.c',
+ 'smimever.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ]
+ },
+ {
+ # This is here so Firefox can link this without having to
+ # repeat the list of dependencies.
+ 'target_name': 'smime3_deps',
+ 'type': 'none',
+ 'dependencies': [
+ 'smime',
+ '<(DEPTH)/lib/pkcs12/pkcs12.gyp:pkcs12',
+ '<(DEPTH)/lib/pkcs7/pkcs7.gyp:pkcs7'
+ ],
+ },
+ {
+ 'target_name': 'smime3',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'smime3_deps',
+ '<(DEPTH)/lib/nss/nss.gyp:nss3',
+ '<(DEPTH)/lib/util/util.gyp:nssutil3'
+ ],
+ 'variables': {
+ 'mapfile': 'smime.def'
+ }
+ }
+ ],
+ 'conditions': [
+ [ 'moz_fold_libs==1', {
+ 'targets': [
+ {
+ 'target_name': 'smime3_static',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'smime3_deps',
+ ],
+ }
+ ],
+ }],
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/smime/smime.h b/security/nss/lib/smime/smime.h
new file mode 100644
index 0000000000..e534ef083f
--- /dev/null
+++ b/security/nss/lib/smime/smime.h
@@ -0,0 +1,141 @@
+/* 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/. */
+
+/*
+ * Header file for routines specific to S/MIME. Keep things that are pure
+ * pkcs7 out of here; this is for S/MIME policy, S/MIME interoperability, etc.
+ */
+
+#ifndef _SMIME_H_
+#define _SMIME_H_ 1
+
+#include "cms.h"
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * Initialize the local recording of the user S/MIME cipher preferences.
+ * This function is called once for each cipher, the order being
+ * important (first call records greatest preference, and so on).
+ * When finished, it is called with a "which" of CIPHER_FAMILID_MASK.
+ * If the function is called again after that, it is assumed that
+ * the preferences are being reset, and the old preferences are
+ * discarded.
+ *
+ * XXX This is for a particular user, and right now the storage is
+ * XXX local, static. The preference should be stored elsewhere to allow
+ * XXX for multiple uses of one library? How does SSL handle this;
+ * XXX it has something similar?
+ *
+ * - The "which" values are defined in ciferfam.h (the SMIME_* values,
+ * for example SMIME_DES_CBC_56).
+ * - If "on" is non-zero then the named cipher is enabled, otherwise
+ * it is disabled. (It is not necessary to call the function for
+ * ciphers that are disabled, however, as that is the default.)
+ *
+ * If the cipher preference is successfully recorded, SECSuccess
+ * is returned. Otherwise SECFailure is returned. The only errors
+ * are due to failure allocating memory or bad parameters/calls:
+ * SEC_ERROR_XXX ("which" is not in the S/MIME cipher family)
+ * SEC_ERROR_XXX (function is being called more times than there
+ * are known/expected ciphers)
+ */
+extern SECStatus NSS_SMIMEUtil_EnableCipher(long which, int on);
+
+/*
+ * Initialize the local recording of the S/MIME policy.
+ * This function is called to allow/disallow a particular cipher.
+ *
+ * XXX This is for the current module, I think, so local, static storage
+ * XXX is okay. Is that correct, or could multiple uses of the same
+ * XXX library expect to operate under different policies?
+ *
+ * - The "which" values are defined in ciferfam.h (the SMIME_* values,
+ * for example SMIME_DES_CBC_56).
+ * - If "on" is non-zero then the named cipher is enabled, otherwise
+ * it is disabled.
+ */
+extern SECStatus NSS_SMIMEUtils_AllowCipher(long which, int on);
+
+/*
+ * Does the current policy allow S/MIME decryption of this particular
+ * algorithm and keysize?
+ */
+extern PRBool NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key);
+
+/*
+ * Does the current policy allow *any* S/MIME encryption (or decryption)?
+ *
+ * This tells whether or not *any* S/MIME encryption can be done,
+ * according to policy. Callers may use this to do nicer user interface
+ * (say, greying out a checkbox so a user does not even try to encrypt
+ * a message when they are not allowed to) or for any reason they want
+ * to check whether S/MIME encryption (or decryption, for that matter)
+ * may be done.
+ *
+ * It takes no arguments. The return value is a simple boolean:
+ * PR_TRUE means encryption (or decryption) is *possible*
+ * (but may still fail due to other reasons, like because we cannot
+ * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
+ * PR_FALSE means encryption (or decryption) is not permitted
+ *
+ * There are no errors from this routine.
+ */
+extern PRBool NSS_SMIMEUtil_EncryptionPossible(void);
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities attr value
+ *
+ * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
+ * S/MIME capabilities attribute value.
+ */
+extern SECStatus NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest);
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
+ */
+extern SECStatus NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp,
+ SECItem *dest, CERTCertificate *cert);
+
+/*
+ * NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
+ */
+extern SECStatus NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp,
+ SECItem *dest, CERTCertificate *cert);
+
+/*
+ * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference - find cert marked by EncryptionKeyPreference
+ * attribute
+ */
+extern CERTCertificate *NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb,
+ SECItem *DERekp);
+
+/*
+ * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
+ */
+extern SECStatus
+NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
+ SECOidTag *bulkalgtag, int *keysize);
+
+/*
+ * Return a boolean that indicates whether the underlying library
+ * will perform as the caller expects.
+ *
+ * The only argument is a string, which should be the version
+ * identifier of the NSS library. That string will be compared
+ * against a string that represents the actual build version of
+ * the S/MIME library.
+ */
+extern PRBool NSSSMIME_VersionCheck(const char *importedVersion);
+
+/*
+ * Returns a const string of the S/MIME library version.
+ */
+extern const char *NSSSMIME_GetVersion(void);
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _SECMIME_H_ */
diff --git a/security/nss/lib/smime/smime.rc b/security/nss/lib/smime/smime.rc
new file mode 100644
index 0000000000..ad24732e42
--- /dev/null
+++ b/security/nss/lib/smime/smime.rc
@@ -0,0 +1,68 @@
+/* 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/. */
+
+#include "nss.h"
+#include <winver.h>
+
+#define MY_LIBNAME "smime"
+#define MY_FILEDESCRIPTION "NSS S/MIME Library"
+
+#define STRINGIZE(x) #x
+#define STRINGIZE2(x) STRINGIZE(x)
+#define NSS_VMAJOR_STR STRINGIZE2(NSS_VMAJOR)
+
+#ifdef _DEBUG
+#define MY_DEBUG_STR " (debug)"
+#define MY_FILEFLAGS_1 VS_FF_DEBUG
+#else
+#define MY_DEBUG_STR ""
+#define MY_FILEFLAGS_1 0x0L
+#endif
+#if NSS_BETA
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1|VS_FF_PRERELEASE
+#else
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1
+#endif
+
+#ifdef WINNT
+#define MY_FILEOS VOS_NT_WINDOWS32
+#else
+#define MY_FILEOS VOS__WINDOWS32
+#endif
+
+#define MY_INTERNAL_NAME MY_LIBNAME NSS_VMAJOR_STR
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION NSS_VMAJOR,NSS_VMINOR,NSS_VPATCH,NSS_VBUILD
+ PRODUCTVERSION NSS_VMAJOR,NSS_VMINOR,NSS_VPATCH,NSS_VBUILD
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS MY_FILEFLAGS_2
+ FILEOS MY_FILEOS
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L // not used
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "CompanyName", "Mozilla Foundation\0"
+ VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0"
+ VALUE "FileVersion", NSS_VERSION "\0"
+ VALUE "InternalName", MY_INTERNAL_NAME "\0"
+ VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+ VALUE "ProductName", "Network Security Services\0"
+ VALUE "ProductVersion", NSS_VERSION "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/security/nss/lib/smime/smimemessage.c b/security/nss/lib/smime/smimemessage.c
new file mode 100644
index 0000000000..3073ab2457
--- /dev/null
+++ b/security/nss/lib/smime/smimemessage.c
@@ -0,0 +1,183 @@
+/* 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/. */
+
+/*
+ * SMIME message methods
+ */
+
+#include "cmslocal.h"
+#include "smime.h"
+
+#include "cert.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+#if 0
+/*
+ * NSS_SMIMEMessage_CreateEncrypted - start an S/MIME encrypting context.
+ *
+ * "scert" is the cert for the sender. It will be checked for validity.
+ * "rcerts" are the certs for the recipients. They will also be checked.
+ *
+ * "certdb" is the cert database to use for verifying the certs.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed 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().)
+ */
+NSSCMSMessage *
+NSS_SMIMEMessage_CreateEncrypted(CERTCertificate *scert,
+ CERTCertificate **rcerts,
+ CERTCertDBHandle *certdb,
+ PK11PasswordFunc pwfn,
+ void *pwfn_arg)
+{
+ NSSCMSMessage *cmsg;
+ long cipher;
+ SECOidTag encalg;
+ int keysize;
+ int mapi, rci;
+
+ cipher = smime_choose_cipher (scert, rcerts);
+ if (cipher < 0)
+ return NULL;
+
+ mapi = smime_mapi_by_cipher (cipher);
+ if (mapi < 0)
+ return NULL;
+
+ /*
+ * XXX This is stretching it -- CreateEnvelopedData should probably
+ * take a cipher itself of some sort, because we cannot know what the
+ * future will bring in terms of parameters for each type of algorithm.
+ * For example, just an algorithm and keysize is *not* sufficient to
+ * fully specify the usage of RC5 (which also needs to know rounds and
+ * block size). Work this out into a better API!
+ */
+ encalg = smime_cipher_map[mapi].algtag;
+ keysize = smime_keysize_by_cipher (cipher);
+ if (keysize < 0)
+ return NULL;
+
+ cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
+ certdb, encalg, keysize,
+ pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ for (rci = 0; rcerts[rci] != NULL; rci++) {
+ if (rcerts[rci] == scert)
+ continue;
+ if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
+ NULL) != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+ }
+
+ return cinfo;
+}
+
+
+/*
+ * Start an S/MIME signing context.
+ *
+ * "scert" is the cert that will be used to sign the data. It will be
+ * checked for validity.
+ *
+ * "ecert" is the signer's encryption cert. If it is different from
+ * scert, then it will be included in the signed message so that the
+ * recipient can save it for future encryptions.
+ *
+ * "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).
+ * XXX There should be SECMIME functions for hashing, or the hashing should
+ * be built into this interface, which we would like because we would
+ * support more smartcards that way, and then this argument should go away.)
+ *
+ * "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.
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed 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().)
+ */
+
+NSSCMSMessage *
+NSS_SMIMEMessage_CreateSigned(CERTCertificate *scert,
+ CERTCertificate *ecert,
+ CERTCertDBHandle *certdb,
+ SECOidTag digestalgtag,
+ SECItem *digest,
+ PK11PasswordFunc pwfn,
+ void *pwfn_arg)
+{
+ NSSCMSMessage *cmsg;
+ NSSCMSSignedData *sigd;
+ NSSCMSSignerInfo *signerinfo;
+
+ /* See note in header comment above about digestalg. */
+ /* Doesn't explain this. PORT_Assert (digestalgtag == SEC_OID_SHA1); */
+
+ cmsg = NSS_CMSMessage_Create(NULL);
+ if (cmsg == NULL)
+ return NULL;
+
+ sigd = NSS_CMSSignedData_Create(cmsg);
+ if (sigd == NULL)
+ goto loser;
+
+ /* create just one signerinfo */
+ signerinfo = NSS_CMSSignerInfo_Create(cmsg, scert, digestalgtag);
+ if (signerinfo == NULL)
+ goto loser;
+
+ /* Add the signing time to the signerinfo. */
+ if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess)
+ goto loser;
+
+ /* and add the SMIME profile */
+ if (NSS_SMIMESignerInfo_AddSMIMEProfile(signerinfo, scert) != SECSuccess)
+ goto loser;
+
+ /* now add the signerinfo to the signeddata */
+ if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess)
+ goto loser;
+
+ /* include the signing cert and its entire chain */
+ /* note that there are no checks for duplicate certs in place, as all the */
+ /* essential data structures (like set of certificate) are not there */
+ if (NSS_CMSSignedData_AddCertChain(sigd, scert) != SECSuccess)
+ goto loser;
+
+ /* If the encryption cert and the signing cert differ, then include
+ * the encryption cert too. */
+ if ( ( ecert != NULL ) && ( ecert != scert ) ) {
+ if (NSS_CMSSignedData_AddCertificate(sigd, ecert) != SECSuccess)
+ goto loser;
+ }
+
+ return cmsg;
+loser:
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+}
+#endif
diff --git a/security/nss/lib/smime/smimesym.c b/security/nss/lib/smime/smimesym.c
new file mode 100644
index 0000000000..2ae39f4002
--- /dev/null
+++ b/security/nss/lib/smime/smimesym.c
@@ -0,0 +1,12 @@
+/* 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/. */
+
+extern void SEC_PKCS7DecodeItem();
+extern void SEC_PKCS7DestroyContentInfo();
+
+smime_CMDExports()
+{
+ SEC_PKCS7DecodeItem();
+ SEC_PKCS7DestroyContentInfo();
+}
diff --git a/security/nss/lib/smime/smimeutil.c b/security/nss/lib/smime/smimeutil.c
new file mode 100644
index 0000000000..2056195dbd
--- /dev/null
+++ b/security/nss/lib/smime/smimeutil.c
@@ -0,0 +1,795 @@
+/* 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/. */
+
+/*
+ * Stuff specific to S/MIME policy and interoperability.
+ */
+
+#include "secmime.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "ciferfam.h" /* for CIPHER_FAMILY symbols */
+#include "secasn1.h"
+#include "secitem.h"
+#include "cert.h"
+#include "keyhi.h"
+#include "secerr.h"
+#include "cms.h"
+#include "nss.h"
+
+SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
+
+/* various integer's ASN.1 encoding */
+static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
+static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
+static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
+
+/* RC2 algorithm parameters (used in smime_cipher_map) */
+static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
+static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
+static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
+
+/*
+ * XXX Would like the "parameters" field to be a SECItem *, but the
+ * encoder is having trouble with optional pointers to an ANY. Maybe
+ * once that is fixed, can change this back...
+ */
+typedef struct {
+ SECItem capabilityID;
+ SECItem parameters;
+ long cipher; /* optimization */
+} NSSSMIMECapability;
+
+static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSSMIMECapability) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(NSSSMIMECapability, capabilityID) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
+ offsetof(NSSSMIMECapability, parameters) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
+};
+
+/*
+ * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
+ * to store this and only this certificate permanently for the sender email address.
+ */
+typedef enum {
+ NSSSMIMEEncryptionKeyPref_IssuerSN,
+ NSSSMIMEEncryptionKeyPref_RKeyID,
+ NSSSMIMEEncryptionKeyPref_SubjectKeyID
+} NSSSMIMEEncryptionKeyPrefSelector;
+
+typedef struct {
+ NSSSMIMEEncryptionKeyPrefSelector selector;
+ union {
+ CERTIssuerAndSN *issuerAndSN;
+ NSSCMSRecipientKeyIdentifier *recipientKeyID;
+ SECItem *subjectKeyID;
+ } id;
+} NSSSMIMEEncryptionKeyPreference;
+
+extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
+
+static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
+ { SEC_ASN1_CHOICE,
+ offsetof(NSSSMIMEEncryptionKeyPreference, selector), NULL,
+ sizeof(NSSSMIMEEncryptionKeyPreference) },
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0 | SEC_ASN1_CONSTRUCTED,
+ offsetof(NSSSMIMEEncryptionKeyPreference, id.issuerAndSN),
+ SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
+ NSSSMIMEEncryptionKeyPref_IssuerSN },
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED,
+ offsetof(NSSSMIMEEncryptionKeyPreference, id.recipientKeyID),
+ NSSCMSRecipientKeyIdentifierTemplate,
+ NSSSMIMEEncryptionKeyPref_RKeyID },
+ { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2 | SEC_ASN1_CONSTRUCTED,
+ offsetof(NSSSMIMEEncryptionKeyPreference, id.subjectKeyID),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate),
+ NSSSMIMEEncryptionKeyPref_SubjectKeyID },
+ { 0 }
+};
+
+/* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
+typedef struct {
+ unsigned long cipher;
+ SECOidTag algtag;
+ SECItem *parms;
+ PRBool enabled; /* in the user's preferences */
+ PRBool allowed; /* per export policy */
+} smime_cipher_map_entry;
+
+/* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
+static smime_cipher_map_entry smime_cipher_map[] = {
+ /* cipher, algtag, parms, enabled, allowed */
+ /* --------------------------------------- */
+ { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_int40, PR_TRUE, PR_TRUE },
+ { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
+ { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_TRUE, PR_TRUE },
+ { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
+ { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
+ { SMIME_AES_CBC_128, SEC_OID_AES_128_CBC, NULL, PR_TRUE, PR_TRUE },
+ { SMIME_AES_CBC_256, SEC_OID_AES_256_CBC, NULL, PR_TRUE, PR_TRUE }
+};
+static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
+
+/*
+ * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
+ */
+static int
+smime_mapi_by_cipher(unsigned long cipher)
+{
+ int i;
+
+ for (i = 0; i < smime_cipher_map_count; i++) {
+ if (smime_cipher_map[i].cipher == cipher)
+ return i; /* bingo */
+ }
+ return -1; /* should not happen if we're consistent, right? */
+}
+
+/*
+ * NSS_SMIME_EnableCipher - this function locally records the user's preference
+ */
+SECStatus
+NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
+{
+ unsigned long mask;
+ int mapi;
+
+ mask = which & CIPHER_FAMILYID_MASK;
+
+ PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
+ if (mask != CIPHER_FAMILYID_SMIME)
+ /* XXX set an error! */
+ return SECFailure;
+
+ mapi = smime_mapi_by_cipher(which);
+ if (mapi < 0)
+ /* XXX set an error */
+ return SECFailure;
+
+ /* do we try to turn on a forbidden cipher? */
+ if (!smime_cipher_map[mapi].allowed && on) {
+ PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
+ return SECFailure;
+ }
+
+ if (smime_cipher_map[mapi].enabled != on)
+ smime_cipher_map[mapi].enabled = on;
+
+ return SECSuccess;
+}
+
+/*
+ * this function locally records the export policy
+ */
+SECStatus
+NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
+{
+ unsigned long mask;
+ int mapi;
+
+ mask = which & CIPHER_FAMILYID_MASK;
+
+ PORT_Assert(mask == CIPHER_FAMILYID_SMIME);
+ if (mask != CIPHER_FAMILYID_SMIME)
+ /* XXX set an error! */
+ return SECFailure;
+
+ mapi = smime_mapi_by_cipher(which);
+ if (mapi < 0)
+ /* XXX set an error */
+ return SECFailure;
+
+ if (smime_cipher_map[mapi].allowed != on)
+ smime_cipher_map[mapi].allowed = on;
+
+ return SECSuccess;
+}
+
+/*
+ * Based on the given algorithm (including its parameters, in some cases!)
+ * and the given key (may or may not be inspected, depending on the
+ * algorithm), find the appropriate policy algorithm specification
+ * and return it. If no match can be made, -1 is returned.
+ */
+static SECStatus
+nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key,
+ unsigned long *cipher)
+{
+ SECOidTag algtag;
+ unsigned int keylen_bits;
+ unsigned long c;
+
+ algtag = SECOID_GetAlgorithmTag(algid);
+ switch (algtag) {
+ case SEC_OID_RC2_CBC:
+ keylen_bits = PK11_GetKeyStrength(key, algid);
+ switch (keylen_bits) {
+ case 40:
+ c = SMIME_RC2_CBC_40;
+ break;
+ case 64:
+ c = SMIME_RC2_CBC_64;
+ break;
+ case 128:
+ c = SMIME_RC2_CBC_128;
+ break;
+ default:
+ return SECFailure;
+ }
+ break;
+ case SEC_OID_DES_CBC:
+ c = SMIME_DES_CBC_56;
+ break;
+ case SEC_OID_DES_EDE3_CBC:
+ c = SMIME_DES_EDE3_168;
+ break;
+ case SEC_OID_AES_128_CBC:
+ c = SMIME_AES_CBC_128;
+ break;
+ case SEC_OID_AES_256_CBC:
+ c = SMIME_AES_CBC_256;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+ *cipher = c;
+ return SECSuccess;
+}
+
+static PRBool
+nss_smime_cipher_allowed(unsigned long which)
+{
+ int mapi;
+
+ mapi = smime_mapi_by_cipher(which);
+ if (mapi < 0)
+ return PR_FALSE;
+ return smime_cipher_map[mapi].allowed;
+}
+
+PRBool
+NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
+{
+ unsigned long which;
+
+ if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
+ return PR_FALSE;
+
+ return nss_smime_cipher_allowed(which);
+}
+
+/*
+ * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
+ *
+ * This tells whether or not *any* S/MIME encryption can be done,
+ * according to policy. Callers may use this to do nicer user interface
+ * (say, greying out a checkbox so a user does not even try to encrypt
+ * a message when they are not allowed to) or for any reason they want
+ * to check whether S/MIME encryption (or decryption, for that matter)
+ * may be done.
+ *
+ * It takes no arguments. The return value is a simple boolean:
+ * PR_TRUE means encryption (or decryption) is *possible*
+ * (but may still fail due to other reasons, like because we cannot
+ * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
+ * PR_FALSE means encryption (or decryption) is not permitted
+ *
+ * There are no errors from this routine.
+ */
+PRBool
+NSS_SMIMEUtil_EncryptionPossible(void)
+{
+ int i;
+
+ for (i = 0; i < smime_cipher_map_count; i++) {
+ if (smime_cipher_map[i].allowed)
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static int
+nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
+{
+ int i;
+ SECOidTag capIDTag;
+
+ /* we need the OIDTag here */
+ capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
+
+ /* go over all the SMIME ciphers we know and see if we find a match */
+ for (i = 0; i < smime_cipher_map_count; i++) {
+ if (smime_cipher_map[i].algtag != capIDTag)
+ continue;
+ /*
+ * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
+ * 2 NULLs as equal and NULL and non-NULL as not equal), we could
+ * use that here instead of all of the following comparison code.
+ */
+ if (!smime_cipher_map[i].parms) {
+ if (!cap->parameters.data || !cap->parameters.len)
+ break; /* both empty: bingo */
+ if (cap->parameters.len == 2 &&
+ cap->parameters.data[0] == SEC_ASN1_NULL &&
+ cap->parameters.data[1] == 0)
+ break; /* DER NULL == NULL, bingo */
+ } else if (cap->parameters.data != NULL &&
+ cap->parameters.len == smime_cipher_map[i].parms->len &&
+ PORT_Memcmp(cap->parameters.data, smime_cipher_map[i].parms->data,
+ cap->parameters.len) == 0) {
+ break; /* both not empty, same length & equal content: bingo */
+ }
+ }
+
+ if (i == smime_cipher_map_count)
+ return 0; /* no match found */
+ return smime_cipher_map[i].cipher; /* match found, point to cipher */
+}
+
+/*
+ * smime_choose_cipher - choose a cipher that works for all the recipients
+ *
+ * "scert" - sender's certificate
+ * "rcerts" - recipient's certificates
+ */
+static long
+smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
+{
+ PLArenaPool *poolp;
+ long cipher;
+ long chosen_cipher;
+ int *cipher_abilities;
+ int *cipher_votes;
+ int weak_mapi;
+ int strong_mapi;
+ int aes128_mapi;
+ int aes256_mapi;
+ int rcount, mapi, max, i;
+
+ chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
+ weak_mapi = smime_mapi_by_cipher(chosen_cipher);
+ aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
+ aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
+
+ poolp = PORT_NewArena(1024); /* XXX what is right value? */
+ if (poolp == NULL)
+ goto done;
+
+ cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
+ cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
+ if (cipher_votes == NULL || cipher_abilities == NULL)
+ goto done;
+
+ /* Make triple-DES the strong cipher. */
+ strong_mapi = smime_mapi_by_cipher(SMIME_DES_EDE3_168);
+
+ /* walk all the recipient's certs */
+ for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
+ SECItem *profile;
+ NSSSMIMECapability **caps;
+ int pref;
+
+ /* the first cipher that matches in the user's SMIME profile gets
+ * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
+ * and so on. If every cipher matches, the last one gets 1 (one) vote */
+ pref = smime_cipher_map_count;
+
+ /* find recipient's SMIME profile */
+ profile = CERT_FindSMimeProfile(rcerts[rcount]);
+
+ if (profile != NULL && profile->data != NULL && profile->len > 0) {
+ /* we have a profile (still DER-encoded) */
+ caps = NULL;
+ /* decode it */
+ if (SEC_QuickDERDecodeItem(poolp, &caps,
+ NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
+ caps != NULL) {
+ /* walk the SMIME capabilities for this recipient */
+ for (i = 0; caps[i] != NULL; i++) {
+ cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
+ mapi = smime_mapi_by_cipher(cipher);
+ if (mapi >= 0) {
+ /* found the cipher */
+ cipher_abilities[mapi]++;
+ cipher_votes[mapi] += pref;
+ --pref;
+ }
+ }
+ }
+ } else {
+ /* no profile found - so we can only assume that the user can do
+ * the mandatory algorithms which are RC2-40 (weak crypto) and
+ * 3DES (strong crypto), unless the user has an elliptic curve
+ * key. For elliptic curve keys, RFC 5753 mandates support
+ * for AES 128 CBC. */
+ SECKEYPublicKey *key;
+ unsigned int pklen_bits;
+ KeyType key_type;
+
+ /*
+ * if recipient's public key length is > 512, vote for a strong cipher
+ * please not that the side effect of this is that if only one recipient
+ * has an export-level public key, the strong cipher is disabled.
+ *
+ * XXX This is probably only good for RSA keys. What I would
+ * really like is a function to just say; Is the public key in
+ * this cert an export-length key? Then I would not have to
+ * know things like the value 512, or the kind of key, or what
+ * a subjectPublicKeyInfo is, etc.
+ */
+ key = CERT_ExtractPublicKey(rcerts[rcount]);
+ pklen_bits = 0;
+ key_type = nullKey;
+ if (key != NULL) {
+ pklen_bits = SECKEY_PublicKeyStrengthInBits(key);
+ key_type = SECKEY_GetPublicKeyType(key);
+ SECKEY_DestroyPublicKey(key);
+ key = NULL;
+ }
+
+ if (key_type == ecKey) {
+ /* While RFC 5753 mandates support for AES-128 CBC, should use
+ * AES 256 if user's key provides more than 128 bits of
+ * security strength so that symmetric key is not weak link. */
+
+ /* RC2-40 is not compatible with elliptic curve keys. */
+ chosen_cipher = SMIME_DES_EDE3_168;
+ if (pklen_bits > 256) {
+ cipher_abilities[aes256_mapi]++;
+ cipher_votes[aes256_mapi] += pref;
+ pref--;
+ }
+ cipher_abilities[aes128_mapi]++;
+ cipher_votes[aes128_mapi] += pref;
+ pref--;
+ cipher_abilities[strong_mapi]++;
+ cipher_votes[strong_mapi] += pref;
+ pref--;
+ } else {
+ if (pklen_bits > 3072) {
+ /* While support for AES 256 is a SHOULD+ in RFC 5751
+ * rather than a MUST, RSA and DSA keys longer than 3072
+ * bits provide more than 128 bits of security strength.
+ * So, AES 256 should be used to provide comparable
+ * security. */
+ cipher_abilities[aes256_mapi]++;
+ cipher_votes[aes256_mapi] += pref;
+ pref--;
+ }
+ if (pklen_bits > 1023) {
+ /* RFC 5751 mandates support for AES 128, but also says
+ * that RSA and DSA signature keys SHOULD NOT be less than
+ * 1024 bits. So, cast vote for AES 128 if key length
+ * is at least 1024 bits. */
+ cipher_abilities[aes128_mapi]++;
+ cipher_votes[aes128_mapi] += pref;
+ pref--;
+ }
+ if (pklen_bits > 512) {
+ /* cast votes for the strong algorithm */
+ cipher_abilities[strong_mapi]++;
+ cipher_votes[strong_mapi] += pref;
+ pref--;
+ }
+
+ /* always cast (possibly less) votes for the weak algorithm */
+ cipher_abilities[weak_mapi]++;
+ cipher_votes[weak_mapi] += pref;
+ }
+ }
+ if (profile != NULL)
+ SECITEM_FreeItem(profile, PR_TRUE);
+ }
+
+ /* find cipher that is agreeable by all recipients and that has the most votes */
+ max = 0;
+ for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
+ /* if not all of the recipients can do this, forget it */
+ if (cipher_abilities[mapi] != rcount)
+ continue;
+ /* if cipher is not enabled or not allowed by policy, forget it */
+ if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
+ continue;
+ /* now see if this one has more votes than the last best one */
+ if (cipher_votes[mapi] >= max) {
+ /* if equal number of votes, prefer the ones further down in the list */
+ /* with the expectation that these are higher rated ciphers */
+ chosen_cipher = smime_cipher_map[mapi].cipher;
+ max = cipher_votes[mapi];
+ }
+ }
+ /* if no common cipher was found, chosen_cipher stays at the default */
+
+done:
+ if (poolp != NULL)
+ PORT_FreeArena(poolp, PR_FALSE);
+
+ return chosen_cipher;
+}
+
+/*
+ * XXX This is a hack for now to satisfy our current interface.
+ * Eventually, with more parameters needing to be specified, just
+ * looking up the keysize is not going to be sufficient.
+ */
+static int
+smime_keysize_by_cipher(unsigned long which)
+{
+ int keysize;
+
+ switch (which) {
+ case SMIME_RC2_CBC_40:
+ keysize = 40;
+ break;
+ case SMIME_RC2_CBC_64:
+ keysize = 64;
+ break;
+ case SMIME_RC2_CBC_128:
+ case SMIME_AES_CBC_128:
+ keysize = 128;
+ break;
+ case SMIME_AES_CBC_256:
+ keysize = 256;
+ break;
+ case SMIME_DES_CBC_56:
+ case SMIME_DES_EDE3_168:
+ /*
+ * These are special; since the key size is fixed, we actually
+ * want to *avoid* specifying a key size.
+ */
+ keysize = 0;
+ break;
+ default:
+ keysize = -1;
+ break;
+ }
+
+ return keysize;
+}
+
+/*
+ * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
+ *
+ * it would be great for UI purposes if there would be a way to find out which recipients
+ * prevented a strong cipher from being used...
+ */
+SECStatus
+NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
+ SECOidTag *bulkalgtag, int *keysize)
+{
+ unsigned long cipher;
+ int mapi;
+
+ cipher = smime_choose_cipher(NULL, rcerts);
+ mapi = smime_mapi_by_cipher(cipher);
+
+ *bulkalgtag = smime_cipher_map[mapi].algtag;
+ *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
+
+ return SECSuccess;
+}
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
+ *
+ * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
+ * S/MIME capabilities attribute value.
+ *
+ * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
+ * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
+ *
+ * "poolp" - arena pool to create the S/MIME capabilities data on
+ * "dest" - SECItem to put the data in
+ */
+SECStatus
+NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
+{
+ NSSSMIMECapability *cap;
+ NSSSMIMECapability **smime_capabilities;
+ smime_cipher_map_entry *map;
+ SECOidData *oiddata;
+ SECItem *dummy;
+ int i, capIndex;
+
+ /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
+ /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
+ smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1) * sizeof(NSSSMIMECapability *));
+ if (smime_capabilities == NULL)
+ return SECFailure;
+
+ capIndex = 0;
+
+ /* Add all the symmetric ciphers
+ * We walk the cipher list backwards, as it is ordered by increasing strength,
+ * we prefer the stronger cipher over a weaker one, and we have to list the
+ * preferred algorithm first */
+ for (i = smime_cipher_map_count - 1; i >= 0; i--) {
+ /* Find the corresponding entry in the cipher map. */
+ map = &(smime_cipher_map[i]);
+ if (!map->enabled)
+ continue;
+
+ /* get next SMIME capability */
+ cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
+ if (cap == NULL)
+ break;
+ smime_capabilities[capIndex++] = cap;
+
+ oiddata = SECOID_FindOIDByTag(map->algtag);
+ if (oiddata == NULL)
+ break;
+
+ cap->capabilityID.data = oiddata->oid.data;
+ cap->capabilityID.len = oiddata->oid.len;
+ cap->parameters.data = map->parms ? map->parms->data : NULL;
+ cap->parameters.len = map->parms ? map->parms->len : 0;
+ cap->cipher = smime_cipher_map[i].cipher;
+ }
+
+ /* XXX add signature algorithms */
+ /* XXX add key encipherment algorithms */
+
+ smime_capabilities[capIndex] = NULL; /* last one - now encode */
+ dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
+
+ /* now that we have the proper encoded SMIMECapabilities (or not),
+ * free the work data */
+ for (i = 0; smime_capabilities[i] != NULL; i++)
+ PORT_Free(smime_capabilities[i]);
+ PORT_Free(smime_capabilities);
+
+ return (dummy == NULL) ? SECFailure : SECSuccess;
+}
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
+ *
+ * "poolp" - arena pool to create the attr value on
+ * "dest" - SECItem to put the data in
+ * "cert" - certificate that should be marked as preferred encryption key
+ * cert is expected to have been verified for EmailRecipient usage.
+ */
+SECStatus
+NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
+{
+ NSSSMIMEEncryptionKeyPreference ekp;
+ SECItem *dummy = NULL;
+ PLArenaPool *tmppoolp = NULL;
+
+ if (cert == NULL)
+ goto loser;
+
+ tmppoolp = PORT_NewArena(1024);
+ if (tmppoolp == NULL)
+ goto loser;
+
+ /* XXX hardcoded IssuerSN choice for now */
+ ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
+ ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
+ if (ekp.id.issuerAndSN == NULL)
+ goto loser;
+
+ dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
+
+loser:
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+
+ return (dummy == NULL) ? SECFailure : SECSuccess;
+}
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
+ *
+ * "poolp" - arena pool to create the attr value on
+ * "dest" - SECItem to put the data in
+ * "cert" - certificate that should be marked as preferred encryption key
+ * cert is expected to have been verified for EmailRecipient usage.
+ */
+SECStatus
+NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
+{
+ SECItem *dummy = NULL;
+ PLArenaPool *tmppoolp = NULL;
+ CERTIssuerAndSN *isn;
+
+ if (cert == NULL)
+ goto loser;
+
+ tmppoolp = PORT_NewArena(1024);
+ if (tmppoolp == NULL)
+ goto loser;
+
+ isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
+ if (isn == NULL)
+ goto loser;
+
+ dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
+
+loser:
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+
+ return (dummy == NULL) ? SECFailure : SECSuccess;
+}
+
+/*
+ * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
+ * find cert marked by EncryptionKeyPreference attribute
+ *
+ * "certdb" - handle for the cert database to look in
+ * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
+ *
+ * if certificate is supposed to be found among the message's included certificates,
+ * they are assumed to have been imported already.
+ */
+CERTCertificate *
+NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
+{
+ PLArenaPool *tmppoolp = NULL;
+ CERTCertificate *cert = NULL;
+ NSSSMIMEEncryptionKeyPreference ekp;
+
+ tmppoolp = PORT_NewArena(1024);
+ if (tmppoolp == NULL)
+ return NULL;
+
+ /* decode DERekp */
+ if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
+ DERekp) != SECSuccess)
+ goto loser;
+
+ /* find cert */
+ switch (ekp.selector) {
+ case NSSSMIMEEncryptionKeyPref_IssuerSN:
+ cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
+ break;
+ case NSSSMIMEEncryptionKeyPref_RKeyID:
+ case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
+ /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
+ break;
+ default:
+ PORT_Assert(0);
+ }
+loser:
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+
+ return cert;
+}
+
+extern const char __nss_smime_version[];
+
+PRBool
+NSSSMIME_VersionCheck(const char *importedVersion)
+{
+#define NSS_VERSION_VARIABLE __nss_smime_version
+#include "verref.h"
+ /*
+ * This is the secret handshake algorithm.
+ *
+ * This release has a simple version compatibility
+ * check algorithm. This release is not backward
+ * compatible with previous major releases. It is
+ * not compatible with future major, minor, or
+ * patch releases.
+ */
+ return NSS_VersionCheck(importedVersion);
+}
+
+const char *
+NSSSMIME_GetVersion(void)
+{
+ return NSS_VERSION;
+}
diff --git a/security/nss/lib/smime/smimever.c b/security/nss/lib/smime/smimever.c
new file mode 100644
index 0000000000..8c06130aba
--- /dev/null
+++ b/security/nss/lib/smime/smimever.c
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/* Library identity and versioning */
+
+#include "nss.h"
+
+#if defined(DEBUG)
+#define _DEBUG_STRING " (debug)"
+#else
+#define _DEBUG_STRING ""
+#endif
+
+/*
+ * Version information
+ */
+const char __nss_smime_version[] = "Version: NSS " NSS_VERSION _DEBUG_STRING;