summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/smime/cmsmessage.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/smime/cmsmessage.c')
-rw-r--r--security/nss/lib/smime/cmsmessage.c345
1 files changed, 345 insertions, 0 deletions
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;
+}