| /* 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; |
| } |