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 decoding. michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "cert.h" michael@0: #include "key.h" michael@0: #include "secasn1.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "prtime.h" michael@0: #include "secerr.h" michael@0: michael@0: struct NSSCMSDecoderContextStr { michael@0: SEC_ASN1DecoderContext * dcx; /* ASN.1 decoder context */ michael@0: NSSCMSMessage * cmsg; /* backpointer to the root message */ michael@0: SECOidTag type; /* type of message */ michael@0: NSSCMSContent content; /* pointer to message */ michael@0: NSSCMSDecoderContext * childp7dcx; /* inner CMS decoder context */ michael@0: PRBool saw_contents; michael@0: int error; michael@0: NSSCMSContentCallback cb; michael@0: void * cb_arg; michael@0: PRBool first_decoded; michael@0: PRBool need_indefinite_finish; michael@0: }; michael@0: michael@0: struct NSSCMSDecoderDataStr { michael@0: SECItem data; /* must be first */ michael@0: unsigned int totalBufferSize; michael@0: }; michael@0: michael@0: typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData; michael@0: michael@0: static void nss_cms_decoder_update_filter (void *arg, const char *data, michael@0: unsigned long len, int depth, SEC_ASN1EncodingPart data_kind); michael@0: static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx); michael@0: static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx); michael@0: static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx); michael@0: static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, michael@0: const unsigned char *data, unsigned long len, PRBool final); michael@0: static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp); michael@0: michael@0: extern const SEC_ASN1Template NSSCMSMessageTemplate[]; michael@0: michael@0: static NSSCMSDecoderData * michael@0: nss_cms_create_decoder_data(PLArenaPool *poolp) michael@0: { michael@0: NSSCMSDecoderData *decoderData = NULL; michael@0: michael@0: decoderData = (NSSCMSDecoderData *) michael@0: PORT_ArenaAlloc(poolp,sizeof(NSSCMSDecoderData)); michael@0: if (!decoderData) { michael@0: return NULL; michael@0: } michael@0: decoderData->data.data = NULL; michael@0: decoderData->data.len = 0; michael@0: decoderData->totalBufferSize = 0; michael@0: return decoderData; michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_decoder_notify - michael@0: * this is the driver of the decoding process. It gets called by the ASN.1 michael@0: * decoder before and after an object is decoded. michael@0: * at various points in the decoding process, we intercept to set up and do michael@0: * further processing. michael@0: */ michael@0: static void michael@0: nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth) michael@0: { michael@0: NSSCMSDecoderContext *p7dcx; michael@0: NSSCMSContentInfo *rootcinfo, *cinfo; michael@0: PRBool after = !before; michael@0: michael@0: p7dcx = (NSSCMSDecoderContext *)arg; michael@0: rootcinfo = &(p7dcx->cmsg->contentInfo); michael@0: michael@0: /* XXX error handling: need to set p7dcx->error */ michael@0: michael@0: #ifdef CMSDEBUG michael@0: fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth); michael@0: #endif michael@0: michael@0: /* so what are we working on right now? */ michael@0: if (p7dcx->type == SEC_OID_UNKNOWN) { michael@0: /* michael@0: * right now, we are still decoding the OUTER (root) cinfo michael@0: * As soon as we know the inner content type, set up the info, michael@0: * but NO inner decoder or filter. The root decoder handles the first michael@0: * level children by itself - only for encapsulated contents (which michael@0: * are encoded as DER inside of an OCTET STRING) we need to set up a michael@0: * child decoder... michael@0: */ michael@0: if (after && dest == &(rootcinfo->contentType)) { michael@0: p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); michael@0: p7dcx->content = rootcinfo->content; michael@0: /* is this ready already ? need to alloc? */ michael@0: /* XXX yes we need to alloc -- continue here */ michael@0: } michael@0: } else if (NSS_CMSType_IsData(p7dcx->type)) { michael@0: /* this can only happen if the outermost cinfo has DATA in it */ michael@0: /* otherwise, we handle this type implicitely in the inner decoders */ michael@0: michael@0: if (before && dest == &(rootcinfo->content)) { michael@0: /* cause the filter to put the data in the right place... michael@0: ** We want the ASN.1 decoder to deliver the decoded bytes to us michael@0: ** from now on michael@0: */ michael@0: SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, michael@0: nss_cms_decoder_update_filter, michael@0: p7dcx, michael@0: (PRBool)(p7dcx->cb != NULL)); michael@0: } else if (after && dest == &(rootcinfo->content.data)) { michael@0: /* remove the filter */ michael@0: SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); michael@0: } michael@0: } else if (NSS_CMSType_IsWrapper(p7dcx->type)) { michael@0: if (!before || dest != &(rootcinfo->content)) { michael@0: michael@0: if (p7dcx->content.pointer == NULL) michael@0: p7dcx->content = rootcinfo->content; michael@0: michael@0: /* get this data type's inner contentInfo */ michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, michael@0: p7dcx->type); michael@0: michael@0: if (before && dest == &(cinfo->contentType)) { michael@0: /* at this point, set up the &%$&$ back pointer */ michael@0: /* we cannot do it later, because the content itself michael@0: * is optional! */ michael@0: switch (p7dcx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: p7dcx->content.signedData->cmsg = p7dcx->cmsg; michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: p7dcx->content.digestedData->cmsg = p7dcx->cmsg; michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: p7dcx->content.envelopedData->cmsg = p7dcx->cmsg; michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: p7dcx->content.encryptedData->cmsg = p7dcx->cmsg; michael@0: break; michael@0: default: michael@0: p7dcx->content.genericData->cmsg = p7dcx->cmsg; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (before && dest == &(cinfo->rawContent)) { michael@0: /* we want the ASN.1 decoder to deliver the decoded bytes to us michael@0: ** from now on michael@0: */ michael@0: SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, michael@0: nss_cms_decoder_update_filter, michael@0: p7dcx, (PRBool)(p7dcx->cb != NULL)); michael@0: michael@0: michael@0: /* we're right in front of the data */ michael@0: if (nss_cms_before_data(p7dcx) != SECSuccess) { michael@0: SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); michael@0: /* stop all processing */ michael@0: p7dcx->error = PORT_GetError(); michael@0: } michael@0: } michael@0: if (after && dest == &(cinfo->rawContent)) { michael@0: /* we're right after of the data */ michael@0: if (nss_cms_after_data(p7dcx) != SECSuccess) michael@0: p7dcx->error = PORT_GetError(); michael@0: michael@0: /* we don't need to see the contents anymore */ michael@0: SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); michael@0: } michael@0: } michael@0: } else { michael@0: /* unsupported or unknown message type - fail gracefully */ michael@0: p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_before_data - set up the current encoder to receive data michael@0: */ michael@0: static SECStatus michael@0: nss_cms_before_data(NSSCMSDecoderContext *p7dcx) michael@0: { michael@0: SECStatus rv; michael@0: SECOidTag childtype; michael@0: PLArenaPool *poolp; michael@0: NSSCMSDecoderContext *childp7dcx; michael@0: NSSCMSContentInfo *cinfo; michael@0: const SEC_ASN1Template *template; michael@0: void *mark = NULL; michael@0: size_t size; michael@0: michael@0: poolp = p7dcx->cmsg->poolp; michael@0: michael@0: /* call _Decode_BeforeData handlers */ michael@0: switch (p7dcx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: /* we're decoding a signedData, so set up the digests */ michael@0: rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: /* we're encoding a digestedData, so set up the digest */ michael@0: rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Decode_BeforeData( michael@0: p7dcx->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Decode_BeforeData( michael@0: p7dcx->content.encryptedData); michael@0: break; michael@0: default: michael@0: rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type, michael@0: p7dcx->content.genericData); michael@0: } michael@0: if (rv != SECSuccess) michael@0: return SECFailure; michael@0: michael@0: /* ok, now we have a pointer to cinfo */ michael@0: /* find out what kind of data is encapsulated */ michael@0: michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); michael@0: childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: michael@0: if (NSS_CMSType_IsData(childtype)) { michael@0: cinfo->content.pointer = (void *) nss_cms_create_decoder_data(poolp); michael@0: if (cinfo->content.pointer == NULL) michael@0: /* set memory error */ michael@0: return SECFailure; michael@0: michael@0: p7dcx->childp7dcx = NULL; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* set up inner decoder */ michael@0: michael@0: if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL) michael@0: return SECFailure; michael@0: michael@0: childp7dcx = PORT_ZNew(NSSCMSDecoderContext); michael@0: if (childp7dcx == NULL) michael@0: return SECFailure; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: /* allocate space for the stuff we're creating */ michael@0: size = NSS_CMSUtil_GetSizeByTypeTag(childtype); michael@0: childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size); michael@0: if (childp7dcx->content.pointer == NULL) michael@0: goto loser; michael@0: michael@0: /* give the parent a copy of the pointer so that it doesn't get lost */ michael@0: cinfo->content.pointer = childp7dcx->content.pointer; michael@0: michael@0: /* start the child decoder */ michael@0: childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, michael@0: template); michael@0: if (childp7dcx->dcx == NULL) michael@0: goto loser; michael@0: michael@0: /* the new decoder needs to notify, too */ michael@0: SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, michael@0: childp7dcx); michael@0: michael@0: /* tell the parent decoder that it needs to feed us the content data */ michael@0: p7dcx->childp7dcx = childp7dcx; michael@0: michael@0: childp7dcx->type = childtype; /* our type */ michael@0: michael@0: childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */ michael@0: michael@0: /* should the child decoder encounter real data, michael@0: ** it must give it to the caller michael@0: */ michael@0: childp7dcx->cb = p7dcx->cb; michael@0: childp7dcx->cb_arg = p7dcx->cb_arg; michael@0: childp7dcx->first_decoded = PR_FALSE; michael@0: childp7dcx->need_indefinite_finish = PR_FALSE; michael@0: if (childtype == SEC_OID_PKCS7_SIGNED_DATA) { michael@0: childp7dcx->first_decoded = PR_TRUE; michael@0: } michael@0: michael@0: /* now set up the parent to hand decoded data to the next level decoder */ michael@0: p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update; michael@0: p7dcx->cb_arg = childp7dcx; michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if (mark) michael@0: PORT_ArenaRelease(poolp, mark); michael@0: if (childp7dcx) michael@0: PORT_Free(childp7dcx); michael@0: p7dcx->childp7dcx = NULL; michael@0: return SECFailure; michael@0: } michael@0: michael@0: static SECStatus michael@0: nss_cms_after_data(NSSCMSDecoderContext *p7dcx) michael@0: { michael@0: NSSCMSDecoderContext *childp7dcx; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: /* Handle last block. This is necessary to flush out the last bytes michael@0: * of a possibly incomplete block */ michael@0: nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE); michael@0: michael@0: /* finish any "inner" decoders - there's no more data coming... */ michael@0: if (p7dcx->childp7dcx != NULL) { michael@0: childp7dcx = p7dcx->childp7dcx; michael@0: if (childp7dcx->dcx != NULL) { michael@0: /* we started and indefinite sequence somewhere, not complete it */ michael@0: if (childp7dcx->need_indefinite_finish) { michael@0: static const char lbuf[2] = { 0, 0 }; michael@0: NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf)); michael@0: childp7dcx->need_indefinite_finish = PR_FALSE; michael@0: } michael@0: michael@0: if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) { michael@0: /* do what? free content? */ michael@0: rv = SECFailure; michael@0: } else { michael@0: rv = nss_cms_after_end(childp7dcx); michael@0: } michael@0: if (rv != SECSuccess) michael@0: goto done; michael@0: } michael@0: PORT_Free(p7dcx->childp7dcx); michael@0: p7dcx->childp7dcx = NULL; michael@0: } michael@0: michael@0: switch (p7dcx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: /* this will finish the digests and verify */ michael@0: rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Decode_AfterData( michael@0: p7dcx->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: rv = NSS_CMSDigestedData_Decode_AfterData( michael@0: p7dcx->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Decode_AfterData( michael@0: p7dcx->content.encryptedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DATA: michael@0: /* do nothing */ michael@0: break; michael@0: default: michael@0: rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type, michael@0: p7dcx->content.genericData); michael@0: break; michael@0: } michael@0: done: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: nss_cms_after_end(NSSCMSDecoderContext *p7dcx) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: switch (p7dcx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: if (p7dcx->content.signedData) michael@0: rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: if (p7dcx->content.envelopedData) michael@0: rv = NSS_CMSEnvelopedData_Decode_AfterEnd( michael@0: p7dcx->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: if (p7dcx->content.digestedData) michael@0: rv = NSS_CMSDigestedData_Decode_AfterEnd( michael@0: p7dcx->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: if (p7dcx->content.encryptedData) michael@0: rv = NSS_CMSEncryptedData_Decode_AfterEnd( michael@0: p7dcx->content.encryptedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DATA: michael@0: break; michael@0: default: michael@0: rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type, michael@0: p7dcx->content.genericData); michael@0: break; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_decoder_work_data - handle decoded data bytes. michael@0: * michael@0: * This function either decrypts the data if needed, and/or calculates digests michael@0: * on it, then either stores it or passes it on to the next level decoder. michael@0: */ michael@0: static void michael@0: nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, michael@0: const unsigned char *data, unsigned long len, michael@0: PRBool final) michael@0: { michael@0: NSSCMSContentInfo *cinfo; michael@0: unsigned char *buf = NULL; michael@0: unsigned char *dest; michael@0: unsigned int offset; michael@0: SECStatus rv; michael@0: michael@0: /* michael@0: * We should really have data to process, or we should be trying michael@0: * to finish/flush the last block. (This is an overly paranoid michael@0: * check since all callers are in this file and simple inspection michael@0: * proves they do it right. But it could find a bug in future michael@0: * modifications/development, that is why it is here.) michael@0: */ michael@0: PORT_Assert ((data != NULL && len) || final); michael@0: michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); michael@0: if (!cinfo) { michael@0: /* The original programmer didn't expect this to happen */ michael@0: p7dcx->error = SEC_ERROR_LIBRARY_FAILURE; michael@0: goto loser; michael@0: } michael@0: michael@0: if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { michael@0: /* michael@0: * we are decrypting. michael@0: * michael@0: * XXX If we get an error, we do not want to do the digest or callback, michael@0: * but we want to keep decoding. Or maybe we want to stop decoding michael@0: * altogether if there is a callback, because obviously we are not michael@0: * sending the data back and they want to know that. michael@0: */ michael@0: michael@0: unsigned int outlen = 0; /* length of decrypted data */ michael@0: unsigned int buflen; /* length available for decrypted data */ michael@0: michael@0: /* find out about the length of decrypted data */ michael@0: buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final); michael@0: michael@0: /* michael@0: * it might happen that we did not provide enough data for a full michael@0: * block (decryption unit), and that there is no output available michael@0: */ michael@0: michael@0: /* no output available, AND no input? */ michael@0: if (buflen == 0 && len == 0) michael@0: goto loser; /* bail out */ michael@0: michael@0: /* michael@0: * have inner decoder: pass the data on (means inner content type is NOT data) michael@0: * no inner decoder: we have DATA in here: either call callback or store michael@0: */ michael@0: if (buflen != 0) { michael@0: /* there will be some output - need to make room for it */ michael@0: /* allocate buffer from the heap */ michael@0: buf = (unsigned char *)PORT_Alloc(buflen); michael@0: if (buf == NULL) { michael@0: p7dcx->error = SEC_ERROR_NO_MEMORY; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * decrypt incoming data michael@0: * buf can still be NULL here (and buflen == 0) here if we don't expect michael@0: * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to michael@0: * keep track of incoming data michael@0: */ michael@0: rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen, michael@0: data, len, final); michael@0: if (rv != SECSuccess) { michael@0: p7dcx->error = PORT_GetError(); michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_Assert (final || outlen == buflen); michael@0: michael@0: /* swap decrypted data in */ michael@0: data = buf; michael@0: len = outlen; michael@0: } michael@0: michael@0: if (len == 0) michael@0: goto done; /* nothing more to do */ michael@0: michael@0: /* michael@0: * Update the running digests with plaintext bytes (if we need to). michael@0: */ michael@0: if (cinfo->privateInfo && cinfo->privateInfo->digcx) michael@0: NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); michael@0: michael@0: /* at this point, we have the plain decoded & decrypted data michael@0: ** which is either more encoded DER (which we need to hand to the child michael@0: ** decoder) or data we need to hand back to our caller michael@0: */ michael@0: michael@0: /* pass the content back to our caller or */ michael@0: /* feed our freshly decrypted and decoded data into child decoder */ michael@0: if (p7dcx->cb != NULL) { michael@0: (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len); michael@0: } michael@0: #if 1 michael@0: else michael@0: #endif michael@0: if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) { michael@0: /* store it in "inner" data item as well */ michael@0: /* find the DATA item in the encapsulated cinfo and store it there */ michael@0: NSSCMSDecoderData *decoderData = michael@0: (NSSCMSDecoderData *)cinfo->content.pointer; michael@0: SECItem *dataItem = &decoderData->data; michael@0: michael@0: offset = dataItem->len; michael@0: if (dataItem->len+len > decoderData->totalBufferSize) { michael@0: int needLen = (dataItem->len+len) * 2; michael@0: dest = (unsigned char *) michael@0: PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen); michael@0: if (dest == NULL) { michael@0: p7dcx->error = SEC_ERROR_NO_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: if (dataItem->len) { michael@0: PORT_Memcpy(dest, dataItem->data, dataItem->len); michael@0: } michael@0: decoderData->totalBufferSize = needLen; michael@0: dataItem->data = dest; michael@0: } michael@0: michael@0: /* copy it in */ michael@0: PORT_Memcpy(dataItem->data + offset, data, len); michael@0: dataItem->len += len; michael@0: } michael@0: michael@0: done: michael@0: loser: michael@0: if (buf) michael@0: PORT_Free (buf); michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_decoder_update_filter - process ASN.1 data michael@0: * michael@0: * once we have set up a filter in nss_cms_decoder_notify(), michael@0: * all data processed by the ASN.1 decoder is also passed through here. michael@0: * we pass the content bytes (as opposed to length and tag bytes) on to michael@0: * nss_cms_decoder_work_data(). michael@0: */ michael@0: static void michael@0: nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len, michael@0: int depth, SEC_ASN1EncodingPart data_kind) michael@0: { michael@0: NSSCMSDecoderContext *p7dcx; michael@0: michael@0: PORT_Assert (len); /* paranoia */ michael@0: if (len == 0) michael@0: return; michael@0: michael@0: p7dcx = (NSSCMSDecoderContext*)arg; michael@0: michael@0: p7dcx->saw_contents = PR_TRUE; michael@0: michael@0: /* pass on the content bytes only */ michael@0: if (data_kind == SEC_ASN1_Contents) michael@0: nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, michael@0: PR_FALSE); michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message michael@0: * michael@0: * "poolp" - pointer to arena for message, or NULL if new pool should be created michael@0: * "cb", "cb_arg" - callback function and argument for delivery of inner content 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: */ michael@0: NSSCMSDecoderContext * michael@0: NSS_CMSDecoder_Start(PLArenaPool *poolp, michael@0: NSSCMSContentCallback cb, void *cb_arg, michael@0: PK11PasswordFunc pwfn, void *pwfn_arg, michael@0: NSSCMSGetDecryptKeyCallback decrypt_key_cb, michael@0: void *decrypt_key_cb_arg) michael@0: { michael@0: NSSCMSDecoderContext *p7dcx; michael@0: NSSCMSMessage *cmsg; michael@0: michael@0: cmsg = NSS_CMSMessage_Create(poolp); michael@0: if (cmsg == NULL) michael@0: return NULL; michael@0: michael@0: NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, michael@0: decrypt_key_cb_arg, NULL, NULL); michael@0: michael@0: p7dcx = PORT_ZNew(NSSCMSDecoderContext); michael@0: if (p7dcx == NULL) { michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: return NULL; michael@0: } michael@0: michael@0: p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate); michael@0: if (p7dcx->dcx == NULL) { michael@0: PORT_Free (p7dcx); michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: return NULL; michael@0: } michael@0: michael@0: SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx); michael@0: michael@0: p7dcx->cmsg = cmsg; michael@0: p7dcx->type = SEC_OID_UNKNOWN; michael@0: michael@0: p7dcx->cb = cb; michael@0: p7dcx->cb_arg = cb_arg; michael@0: p7dcx->first_decoded = PR_FALSE; michael@0: p7dcx->need_indefinite_finish = PR_FALSE; michael@0: return p7dcx; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSDecoder_Update - feed DER-encoded data to decoder michael@0: */ michael@0: SECStatus michael@0: NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, michael@0: unsigned long len) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: if (p7dcx->dcx != NULL && p7dcx->error == 0) { michael@0: /* if error is set already, don't bother */ michael@0: if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) michael@0: && (p7dcx->first_decoded==PR_TRUE) michael@0: && (buf[0] == SEC_ASN1_INTEGER)) { michael@0: /* Microsoft Windows 2008 left out the Sequence wrapping in some michael@0: * of their kerberos replies. If we are here, we most likely are michael@0: * dealing with one of those replies. Supply the Sequence wrap michael@0: * as indefinite encoding (since we don't know the total length michael@0: * yet) */ michael@0: static const char lbuf[2] = michael@0: { SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED, 0x80 }; michael@0: rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf)); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: /* ok, we're going to need the indefinite finish when we are done */ michael@0: p7dcx->need_indefinite_finish = PR_TRUE; michael@0: } michael@0: michael@0: rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len); michael@0: } michael@0: michael@0: loser: michael@0: p7dcx->first_decoded = PR_FALSE; michael@0: if (rv != SECSuccess) { michael@0: p7dcx->error = PORT_GetError(); michael@0: PORT_Assert (p7dcx->error); michael@0: if (p7dcx->error == 0) michael@0: p7dcx->error = -1; michael@0: } michael@0: michael@0: if (p7dcx->error == 0) michael@0: return SECSuccess; michael@0: michael@0: /* there has been a problem, let's finish the decoder */ michael@0: if (p7dcx->dcx != NULL) { michael@0: (void) SEC_ASN1DecoderFinish (p7dcx->dcx); michael@0: p7dcx->dcx = NULL; michael@0: } michael@0: PORT_SetError (p7dcx->error); michael@0: michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSDecoder_Cancel - stop decoding in case of error michael@0: */ michael@0: void michael@0: NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx) michael@0: { michael@0: if (p7dcx->dcx != NULL) michael@0: (void)SEC_ASN1DecoderFinish(p7dcx->dcx); michael@0: NSS_CMSMessage_Destroy(p7dcx->cmsg); michael@0: PORT_Free(p7dcx); michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding michael@0: */ michael@0: NSSCMSMessage * michael@0: NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx) michael@0: { michael@0: NSSCMSMessage *cmsg; michael@0: michael@0: cmsg = p7dcx->cmsg; michael@0: michael@0: if (p7dcx->dcx == NULL || michael@0: SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess || michael@0: nss_cms_after_end(p7dcx) != SECSuccess) michael@0: { michael@0: NSS_CMSMessage_Destroy(cmsg); /* get rid of pool if it's ours */ michael@0: cmsg = NULL; michael@0: } michael@0: michael@0: PORT_Free(p7dcx); michael@0: return cmsg; michael@0: } michael@0: michael@0: NSSCMSMessage * michael@0: NSS_CMSMessage_CreateFromDER(SECItem *DERmessage, michael@0: NSSCMSContentCallback cb, void *cb_arg, michael@0: PK11PasswordFunc pwfn, void *pwfn_arg, michael@0: NSSCMSGetDecryptKeyCallback decrypt_key_cb, michael@0: void *decrypt_key_cb_arg) michael@0: { michael@0: NSSCMSDecoderContext *p7dcx; michael@0: michael@0: /* first arg(poolp) == NULL => create our own pool */ michael@0: p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, michael@0: decrypt_key_cb, decrypt_key_cb_arg); michael@0: if (p7dcx == NULL) michael@0: return NULL; michael@0: NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len); michael@0: return NSS_CMSDecoder_Finish(p7dcx); michael@0: } michael@0: