michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * CMS message methods. michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "cert.h" michael@0: #include "secasn1.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: michael@0: /* michael@0: * NSS_CMSMessage_Create - create a CMS message object michael@0: * michael@0: * "poolp" - arena to allocate memory from, or NULL if new arena should be created michael@0: */ michael@0: NSSCMSMessage * michael@0: NSS_CMSMessage_Create(PLArenaPool *poolp) michael@0: { michael@0: void *mark = NULL; michael@0: NSSCMSMessage *cmsg; michael@0: PRBool poolp_is_ours = PR_FALSE; michael@0: michael@0: if (poolp == NULL) { michael@0: poolp = PORT_NewArena (1024); /* XXX what is right value? */ michael@0: if (poolp == NULL) michael@0: return NULL; michael@0: poolp_is_ours = PR_TRUE; michael@0: } michael@0: michael@0: if (!poolp_is_ours) michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: cmsg = (NSSCMSMessage *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSMessage)); michael@0: if (cmsg == NULL) { michael@0: if (!poolp_is_ours) { michael@0: if (mark) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: } michael@0: } else michael@0: PORT_FreeArena(poolp, PR_FALSE); michael@0: return NULL; michael@0: } michael@0: NSS_CMSContentInfo_Private_Init(&(cmsg->contentInfo)); michael@0: michael@0: cmsg->poolp = poolp; michael@0: cmsg->poolp_is_ours = poolp_is_ours; michael@0: cmsg->refCount = 1; michael@0: michael@0: if (mark) michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: michael@0: return cmsg; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding michael@0: * michael@0: * "cmsg" - message object michael@0: * "pwfn", pwfn_arg" - callback function for getting token password michael@0: * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData michael@0: * "detached_digestalgs", "detached_digests" - digests from detached content michael@0: */ michael@0: void michael@0: NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg, michael@0: PK11PasswordFunc pwfn, void *pwfn_arg, michael@0: NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, michael@0: SECAlgorithmID **detached_digestalgs, SECItem **detached_digests) michael@0: { michael@0: if (pwfn) michael@0: PK11_SetPasswordFunc(pwfn); michael@0: cmsg->pwfn_arg = pwfn_arg; michael@0: cmsg->decrypt_key_cb = decrypt_key_cb; michael@0: cmsg->decrypt_key_cb_arg = decrypt_key_cb_arg; michael@0: cmsg->detached_digestalgs = detached_digestalgs; michael@0: cmsg->detached_digests = detached_digests; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces. michael@0: */ michael@0: void michael@0: NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg) michael@0: { michael@0: PORT_Assert (cmsg->refCount > 0); michael@0: if (cmsg->refCount <= 0) /* oops */ michael@0: return; michael@0: michael@0: cmsg->refCount--; /* thread safety? */ michael@0: if (cmsg->refCount > 0) michael@0: return; michael@0: michael@0: NSS_CMSContentInfo_Destroy(&(cmsg->contentInfo)); michael@0: michael@0: /* if poolp is not NULL, cmsg is the owner of its arena */ michael@0: if (cmsg->poolp_is_ours) michael@0: PORT_FreeArena (cmsg->poolp, PR_FALSE); /* XXX clear it? */ michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_Copy - return a copy of the given message. michael@0: * michael@0: * The copy may be virtual or may be real -- either way, the result needs michael@0: * to be passed to NSS_CMSMessage_Destroy later (as does the original). michael@0: */ michael@0: NSSCMSMessage * michael@0: NSS_CMSMessage_Copy(NSSCMSMessage *cmsg) michael@0: { michael@0: if (cmsg == NULL) michael@0: return NULL; michael@0: michael@0: PORT_Assert (cmsg->refCount > 0); michael@0: michael@0: cmsg->refCount++; /* XXX chrisk thread safety? */ michael@0: return cmsg; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_GetArena - return a pointer to the message's arena pool michael@0: */ michael@0: PLArenaPool * michael@0: NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg) michael@0: { michael@0: return cmsg->poolp; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo michael@0: */ michael@0: NSSCMSContentInfo * michael@0: NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg) michael@0: { michael@0: return &(cmsg->contentInfo); michael@0: } michael@0: michael@0: /* michael@0: * Return a pointer to the actual content. michael@0: * In the case of those types which are encrypted, this returns the *plain* content. michael@0: * In case of nested contentInfos, this descends and retrieves the innermost content. michael@0: */ michael@0: SECItem * michael@0: NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg) michael@0: { michael@0: /* this is a shortcut */ michael@0: NSSCMSContentInfo * cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: SECItem * pItem = NSS_CMSContentInfo_GetInnerContent(cinfo); michael@0: return pItem; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message michael@0: * michael@0: * CMS data content objects do not count. michael@0: */ michael@0: int michael@0: NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg) michael@0: { michael@0: int count = 0; michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* walk down the chain of contentinfos */ michael@0: for (cinfo = &(cmsg->contentInfo); cinfo != NULL; ) { michael@0: count++; michael@0: cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo); michael@0: } michael@0: return count; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_ContentLevel - find content level #n michael@0: * michael@0: * CMS data content objects do not count. michael@0: */ michael@0: NSSCMSContentInfo * michael@0: NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n) michael@0: { michael@0: int count = 0; michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* walk down the chain of contentinfos */ michael@0: for (cinfo = &(cmsg->contentInfo); cinfo != NULL && count < n; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) { michael@0: count++; michael@0: } michael@0: michael@0: return cinfo; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way michael@0: */ michael@0: PRBool michael@0: NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg) michael@0: { michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* descend into CMS message */ michael@0: for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) { michael@0: if (!NSS_CMSType_IsData(NSS_CMSContentInfo_GetContentTypeTag(cinfo))) michael@0: continue; /* next level */ michael@0: michael@0: if (NSS_CMSSignedData_ContainsCertsOrCrls(cinfo->content.signedData)) michael@0: return PR_TRUE; michael@0: /* callback here for generic wrappers? */ michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage michael@0: */ michael@0: PRBool michael@0: NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg) michael@0: { michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* walk down the chain of contentinfos */ michael@0: for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) michael@0: { michael@0: switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) { michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: return PR_TRUE; michael@0: default: michael@0: /* callback here for generic wrappers? */ michael@0: break; michael@0: } michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_IsSigned - see if message contains a signed submessage michael@0: * michael@0: * If the CMS message has a SignedData with a signature (not just a SignedData) michael@0: * return true; false otherwise. This can/should be called before calling michael@0: * VerifySignature, which will always indicate failure if no signature is michael@0: * present, but that does not mean there even was a signature! michael@0: * Note that the content itself can be empty (detached content was sent michael@0: * another way); it is the presence of the signature that matters. michael@0: */ michael@0: PRBool michael@0: NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg) michael@0: { michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* walk down the chain of contentinfos */ michael@0: for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) michael@0: { michael@0: switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos)) michael@0: return PR_TRUE; michael@0: break; michael@0: default: michael@0: /* callback here for generic wrappers? */ michael@0: break; michael@0: } michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSMessage_IsContentEmpty - see if content is empty michael@0: * michael@0: * returns PR_TRUE is innermost content length is < minLen michael@0: * XXX need the encrypted content length (why?) michael@0: */ michael@0: PRBool michael@0: NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen) michael@0: { michael@0: SECItem *item = NULL; michael@0: michael@0: if (cmsg == NULL) michael@0: return PR_TRUE; michael@0: michael@0: item = NSS_CMSContentInfo_GetContent(NSS_CMSMessage_GetContentInfo(cmsg)); michael@0: michael@0: if (!item) { michael@0: return PR_TRUE; michael@0: } else if(item->len <= minLen) { michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: return PR_FALSE; michael@0: }