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 encoding. 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 "secoid.h" michael@0: #include "secitem.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: michael@0: struct nss_cms_encoder_output { michael@0: NSSCMSContentCallback outputfn; michael@0: void *outputarg; michael@0: PLArenaPool *destpoolp; michael@0: SECItem *dest; michael@0: }; michael@0: michael@0: struct NSSCMSEncoderContextStr { michael@0: SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */ michael@0: PRBool ecxupdated; /* true if data was handed in */ michael@0: NSSCMSMessage * cmsg; /* pointer to the root message */ michael@0: SECOidTag type; /* type tag of the current content */ michael@0: NSSCMSContent content; /* pointer to current content */ michael@0: struct nss_cms_encoder_output output; /* output function */ michael@0: int error; /* error code */ michael@0: NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */ michael@0: }; michael@0: michael@0: static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx); michael@0: static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx); michael@0: static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len); michael@0: static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, michael@0: const unsigned char *data, unsigned long len, michael@0: PRBool final, PRBool innermost); michael@0: michael@0: extern const SEC_ASN1Template NSSCMSMessageTemplate[]; michael@0: michael@0: /* michael@0: * The little output function that the ASN.1 encoder calls to hand michael@0: * us bytes which we in turn hand back to our caller (via the callback michael@0: * they gave us). michael@0: */ michael@0: static void michael@0: nss_cms_encoder_out(void *arg, const char *buf, unsigned long len, michael@0: int depth, SEC_ASN1EncodingPart data_kind) michael@0: { michael@0: struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; michael@0: unsigned char *dest; michael@0: unsigned long offset; michael@0: michael@0: #ifdef CMSDEBUG michael@0: int i; michael@0: const char *data_name = "unknown"; michael@0: michael@0: switch (data_kind) { michael@0: case SEC_ASN1_Identifier: michael@0: data_name = "identifier"; michael@0: break; michael@0: case SEC_ASN1_Length: michael@0: data_name = "length"; michael@0: break; michael@0: case SEC_ASN1_Contents: michael@0: data_name = "contents"; michael@0: break; michael@0: case SEC_ASN1_EndOfContents: michael@0: data_name = "end-of-contents"; michael@0: break; michael@0: } michael@0: fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len); michael@0: for (i=0; i < len; i++) { michael@0: fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); michael@0: } michael@0: if ((i % 16) != 0) michael@0: fprintf(stderr, "\n"); michael@0: #endif michael@0: michael@0: if (output->outputfn != NULL) michael@0: /* call output callback with DER data */ michael@0: output->outputfn(output->outputarg, buf, len); michael@0: michael@0: if (output->dest != NULL) { michael@0: /* store DER data in SECItem */ michael@0: offset = output->dest->len; michael@0: if (offset == 0) { michael@0: dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len); michael@0: } else { michael@0: dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, michael@0: output->dest->data, michael@0: output->dest->len, michael@0: output->dest->len + len); michael@0: } michael@0: if (dest == NULL) michael@0: /* oops */ michael@0: return; michael@0: michael@0: output->dest->data = dest; michael@0: output->dest->len += len; michael@0: michael@0: /* copy it in */ michael@0: PORT_Memcpy(output->dest->data + offset, buf, len); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_encoder_notify - ASN.1 encoder callback michael@0: * michael@0: * this function is called by the ASN.1 encoder before and after the encoding of michael@0: * every object. here, it is used to keep track of data structures, set up michael@0: * encryption and/or digesting and possibly set up child encoders. michael@0: */ michael@0: static void michael@0: nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth) michael@0: { michael@0: NSSCMSEncoderContext *p7ecx; michael@0: NSSCMSContentInfo *rootcinfo, *cinfo; michael@0: PRBool after = !before; michael@0: PLArenaPool *poolp; michael@0: SECOidTag childtype; michael@0: SECItem *item; michael@0: michael@0: p7ecx = (NSSCMSEncoderContext *)arg; michael@0: PORT_Assert(p7ecx != NULL); michael@0: michael@0: rootcinfo = &(p7ecx->cmsg->contentInfo); michael@0: poolp = p7ecx->cmsg->poolp; 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: /* michael@0: * Watch for the content field, at which point we want to instruct michael@0: * the ASN.1 encoder to start taking bytes from the buffer. michael@0: */ michael@0: if (NSS_CMSType_IsData(p7ecx->type)) { michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); michael@0: if (before && dest == &(cinfo->rawContent)) { michael@0: /* just set up encoder to grab from user - no encryption or digesting */ michael@0: if ((item = cinfo->content.data) != NULL) michael@0: (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE); michael@0: else michael@0: SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); michael@0: SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ michael@0: } michael@0: } else if (NSS_CMSType_IsWrapper(p7ecx->type)) { michael@0: /* when we know what the content is, we encode happily until we reach the inner content */ michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); michael@0: childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: michael@0: if (after && dest == &(cinfo->contentType)) { michael@0: /* we're right before encoding the data (if we have some or not) */ michael@0: /* (for encrypted data, we're right before the contentEncAlg which may change */ michael@0: /* in nss_cms_before_data because of IV calculation when setting up encryption) */ michael@0: if (nss_cms_before_data(p7ecx) != SECSuccess) michael@0: p7ecx->error = PORT_GetError(); michael@0: } michael@0: if (before && dest == &(cinfo->rawContent)) { michael@0: if (p7ecx->childp7ecx == NULL) { michael@0: if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) { michael@0: /* we are the innermost non-data and we have data - feed it in */ michael@0: (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE); michael@0: } else { michael@0: /* else we'll have to get data from user */ michael@0: SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); michael@0: } michael@0: } else { michael@0: /* if we have a nested encoder, wait for its data */ michael@0: SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); michael@0: } michael@0: } michael@0: if (after && dest == &(cinfo->rawContent)) { michael@0: if (nss_cms_after_data(p7ecx) != SECSuccess) michael@0: p7ecx->error = PORT_GetError(); michael@0: SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ michael@0: } michael@0: } else { michael@0: /* we're still in the root message */ michael@0: if (after && dest == &(rootcinfo->contentType)) { michael@0: /* got the content type OID now - so find out the type tag */ michael@0: p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); michael@0: /* set up a pointer to our current content */ michael@0: p7ecx->content = rootcinfo->content; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_before_data - setup the current encoder to receive data michael@0: */ michael@0: static SECStatus michael@0: nss_cms_before_data(NSSCMSEncoderContext *p7ecx) michael@0: { michael@0: SECStatus rv; michael@0: SECOidTag childtype; michael@0: NSSCMSContentInfo *cinfo; michael@0: PLArenaPool *poolp; michael@0: NSSCMSEncoderContext *childp7ecx; michael@0: const SEC_ASN1Template *template; michael@0: michael@0: poolp = p7ecx->cmsg->poolp; michael@0: michael@0: /* call _Encode_BeforeData handlers */ michael@0: switch (p7ecx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: /* we're encoding a signedData, so set up the digests */ michael@0: rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->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_Encode_BeforeData(p7ecx->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData); michael@0: break; michael@0: default: michael@0: if (NSS_CMSType_IsWrapper(p7ecx->type)) { michael@0: rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, p7ecx->content.genericData); michael@0: } else { michael@0: rv = SECFailure; michael@0: } 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(p7ecx->content.pointer, p7ecx->type); michael@0: childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: michael@0: if (NSS_CMSType_IsWrapper(childtype)) { michael@0: /* in these cases, we need to set up a child encoder! */ michael@0: /* create new encoder context */ michael@0: childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); michael@0: if (childp7ecx == NULL) michael@0: return SECFailure; michael@0: michael@0: /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder michael@0: * (which will encrypt and/or digest it) michael@0: * this needs to route back into our update function michael@0: * which finds the lowest encoding context & encrypts and computes digests */ michael@0: childp7ecx->type = childtype; michael@0: childp7ecx->content = cinfo->content; michael@0: /* use the non-recursive update function here, of course */ michael@0: childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update; michael@0: childp7ecx->output.outputarg = p7ecx; michael@0: childp7ecx->output.destpoolp = NULL; michael@0: childp7ecx->output.dest = NULL; michael@0: childp7ecx->cmsg = p7ecx->cmsg; michael@0: childp7ecx->ecxupdated = PR_FALSE; michael@0: childp7ecx->childp7ecx = NULL; michael@0: michael@0: template = NSS_CMSUtil_GetTemplateByTypeTag(childtype); michael@0: if (template == NULL) michael@0: goto loser; /* cannot happen */ michael@0: michael@0: /* now initialize the data for encoding the first third */ michael@0: switch (childp7ecx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); michael@0: break; michael@0: default: michael@0: rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, cinfo->content.genericData); michael@0: break; michael@0: } michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* michael@0: * Initialize the BER encoder. michael@0: */ michael@0: childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, michael@0: nss_cms_encoder_out, &(childp7ecx->output)); michael@0: if (childp7ecx->ecx == NULL) michael@0: goto loser; michael@0: michael@0: /* michael@0: * Indicate that we are streaming. We will be streaming until we michael@0: * get past the contents bytes. michael@0: */ michael@0: if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) michael@0: SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); michael@0: michael@0: /* michael@0: * The notify function will watch for the contents field. michael@0: */ michael@0: p7ecx->childp7ecx = childp7ecx; michael@0: SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx); michael@0: michael@0: /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ michael@0: /* encoding process - we'll do that from the update function instead */ michael@0: /* otherwise we'd be encoding data from a call of the notify function of the */ michael@0: /* parent encoder (which would not work) */ michael@0: michael@0: } else if (NSS_CMSType_IsData(childtype)) { michael@0: p7ecx->childp7ecx = NULL; michael@0: } else { michael@0: /* we do not know this type */ michael@0: p7ecx->error = SEC_ERROR_BAD_DER; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if (childp7ecx) { michael@0: if (childp7ecx->ecx) michael@0: SEC_ASN1EncoderFinish(childp7ecx->ecx); michael@0: PORT_Free(childp7ecx); michael@0: p7ecx->childp7ecx = NULL; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: static SECStatus michael@0: nss_cms_after_data(NSSCMSEncoderContext *p7ecx) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: switch (p7ecx->type) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: /* this will finish the digests and sign */ michael@0: rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData); michael@0: break; michael@0: default: michael@0: if (NSS_CMSType_IsWrapper(p7ecx->type)) { michael@0: rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, p7ecx->content.genericData); michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: break; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_encoder_work_data - process incoming data michael@0: * michael@0: * (from the user or the next encoding layer) michael@0: * Here, we need to digest and/or encrypt, then pass it on michael@0: */ michael@0: static SECStatus michael@0: nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, michael@0: const unsigned char *data, unsigned long len, michael@0: PRBool final, PRBool innermost) michael@0: { michael@0: unsigned char *buf = NULL; michael@0: SECStatus rv; michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: rv = SECSuccess; /* may as well be optimistic */ 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: /* we got data (either from the caller, or from a lower level encoder) */ michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); michael@0: if (!cinfo) { michael@0: /* The original programmer didn't expect this to happen */ michael@0: p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Update the running digest. */ michael@0: if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL) michael@0: NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); michael@0: michael@0: /* Encrypt this chunk. */ michael@0: if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { michael@0: unsigned int inlen; /* length of data being encrypted */ michael@0: unsigned int outlen; /* length of encrypted data */ michael@0: unsigned int buflen; /* length available for encrypted data */ michael@0: michael@0: inlen = len; michael@0: buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, inlen, final); michael@0: if (buflen == 0) { michael@0: /* michael@0: * No output is expected, but the input data may be buffered michael@0: * so we still have to call Encrypt. michael@0: */ michael@0: rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0, michael@0: data, inlen, final); michael@0: if (final) { michael@0: len = 0; michael@0: goto done; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: if (dest != NULL) michael@0: buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); michael@0: else michael@0: buf = (unsigned char*)PORT_Alloc(buflen); michael@0: michael@0: if (buf == NULL) { michael@0: rv = SECFailure; michael@0: } else { michael@0: rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen, michael@0: data, inlen, final); michael@0: data = buf; michael@0: len = outlen; michael@0: } michael@0: if (rv != SECSuccess) michael@0: /* encryption or malloc failed? */ michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * at this point (data,len) has everything we'd like to give to the CURRENT encoder michael@0: * (which will encode it, then hand it back to the user or the parent encoder) michael@0: * We don't encode the data if we're innermost and we're told not to include the data michael@0: */ michael@0: if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != cinfo->content.pointer)) michael@0: rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); michael@0: michael@0: done: michael@0: michael@0: if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { michael@0: if (dest != NULL) { michael@0: dest->data = buf; michael@0: dest->len = len; michael@0: } else if (buf != NULL) { michael@0: PORT_Free (buf); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * nss_cms_encoder_update - deliver encoded data to the next higher level michael@0: * michael@0: * no recursion here because we REALLY want to end up at the next higher encoder! michael@0: */ michael@0: static SECStatus michael@0: nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) michael@0: { michael@0: /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ michael@0: return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE); michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSEncoder_Start - set up encoding of a CMS message michael@0: * michael@0: * "cmsg" - message to encode michael@0: * "outputfn", "outputarg" - callback function for delivery of DER-encoded output michael@0: * will not be called if NULL. michael@0: * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output michael@0: * "destpoolp" - pool to allocate DER-encoded output in 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: NSSCMSEncoderContext * michael@0: NSS_CMSEncoder_Start(NSSCMSMessage *cmsg, michael@0: NSSCMSContentCallback outputfn, void *outputarg, michael@0: SECItem *dest, PLArenaPool *destpoolp, 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: NSSCMSEncoderContext *p7ecx; michael@0: SECStatus rv; michael@0: NSSCMSContentInfo *cinfo; michael@0: SECOidTag tag; michael@0: michael@0: NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, michael@0: detached_digestalgs, detached_digests); michael@0: michael@0: p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); michael@0: if (p7ecx == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: p7ecx->cmsg = cmsg; michael@0: p7ecx->output.outputfn = outputfn; michael@0: p7ecx->output.outputarg = outputarg; michael@0: p7ecx->output.dest = dest; michael@0: p7ecx->output.destpoolp = destpoolp; michael@0: p7ecx->type = SEC_OID_UNKNOWN; michael@0: michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: michael@0: tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: switch (tag) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); michael@0: break; michael@0: case SEC_OID_PKCS7_DIGESTED_DATA: michael@0: rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); michael@0: break; michael@0: default: michael@0: if (NSS_CMSType_IsWrapper(tag)) { michael@0: rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, michael@0: p7ecx->content.genericData); michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: break; michael@0: } michael@0: if (rv != SECSuccess) { michael@0: PORT_Free(p7ecx); michael@0: return NULL; michael@0: } michael@0: michael@0: /* Initialize the BER encoder. michael@0: * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ michael@0: p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate, michael@0: nss_cms_encoder_out, &(p7ecx->output)); michael@0: if (p7ecx->ecx == NULL) { michael@0: PORT_Free (p7ecx); michael@0: return NULL; michael@0: } michael@0: p7ecx->ecxupdated = PR_FALSE; michael@0: michael@0: /* michael@0: * Indicate that we are streaming. We will be streaming until we michael@0: * get past the contents bytes. michael@0: */ michael@0: if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) michael@0: SEC_ASN1EncoderSetStreaming(p7ecx->ecx); michael@0: michael@0: /* michael@0: * The notify function will watch for the contents field. michael@0: */ michael@0: SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); michael@0: michael@0: /* this will kick off the encoding process & encode everything up to the content bytes, michael@0: * at which point the notify function sets streaming mode (and possibly creates michael@0: * a child encoder). */ michael@0: p7ecx->ecxupdated = PR_TRUE; michael@0: if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { michael@0: PORT_Free (p7ecx); michael@0: return NULL; michael@0: } michael@0: michael@0: return p7ecx; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSEncoder_Update - take content data delivery from the user michael@0: * michael@0: * "p7ecx" - encoder context michael@0: * "data" - content data michael@0: * "len" - length of content data michael@0: * michael@0: * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), michael@0: * then hand the data to the work_data fn michael@0: */ michael@0: SECStatus michael@0: NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) michael@0: { michael@0: SECStatus rv; michael@0: NSSCMSContentInfo *cinfo; michael@0: SECOidTag childtype; michael@0: michael@0: if (p7ecx->error) michael@0: return SECFailure; michael@0: michael@0: /* hand data to the innermost decoder */ michael@0: if (p7ecx->childp7ecx) { michael@0: /* tell the child to start encoding, up to its first data byte, if it michael@0: * hasn't started yet */ michael@0: if (!p7ecx->childp7ecx->ecxupdated) { michael@0: p7ecx->childp7ecx->ecxupdated = PR_TRUE; michael@0: if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess) michael@0: return SECFailure; michael@0: } michael@0: /* recursion here */ michael@0: rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len); michael@0: } else { michael@0: /* we are at innermost decoder */ michael@0: /* find out about our inner content type - must be data */ michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); michael@0: if (!cinfo) { michael@0: /* The original programmer didn't expect this to happen */ michael@0: p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; michael@0: return SECFailure; michael@0: } michael@0: michael@0: childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: if (!NSS_CMSType_IsData(childtype)) michael@0: return SECFailure; michael@0: /* and we must not have preset data */ michael@0: if (cinfo->content.data != NULL) michael@0: return SECFailure; michael@0: michael@0: /* hand it the data so it can encode it (let DER trickle up the chain) */ michael@0: rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSEncoder_Cancel - stop all encoding michael@0: * michael@0: * we need to walk down the chain of encoders and the finish them from the innermost out michael@0: */ michael@0: SECStatus michael@0: NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: /* XXX do this right! */ michael@0: michael@0: /* michael@0: * Finish any inner decoders before us so that all the encoded data is flushed michael@0: * This basically finishes all the decoders from the innermost to the outermost. michael@0: * Finishing an inner decoder may result in data being updated to the outer decoder michael@0: * while we are already in NSS_CMSEncoder_Finish, but that's allright. michael@0: */ michael@0: if (p7ecx->childp7ecx) { michael@0: rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ michael@0: /* remember rv for now */ michael@0: } michael@0: michael@0: /* michael@0: * On the way back up, there will be no more data (if we had an michael@0: * inner encoder, it is done now!) michael@0: * Flush out any remaining data and/or finish digests. michael@0: */ michael@0: rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: p7ecx->childp7ecx = NULL; michael@0: michael@0: /* kick the encoder back into working mode again. michael@0: * We turn off streaming stuff (which will cause the encoder to continue michael@0: * encoding happily, now that we have all the data (like digests) ready for it). michael@0: */ michael@0: SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); michael@0: SEC_ASN1EncoderClearStreaming(p7ecx->ecx); michael@0: michael@0: /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ michael@0: rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); michael@0: michael@0: loser: michael@0: SEC_ASN1EncoderFinish(p7ecx->ecx); michael@0: PORT_Free (p7ecx); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSEncoder_Finish - signal the end of data michael@0: * michael@0: * we need to walk down the chain of encoders and the finish them from the innermost out michael@0: */ michael@0: SECStatus michael@0: NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: NSSCMSContentInfo *cinfo; michael@0: michael@0: /* michael@0: * Finish any inner decoders before us so that all the encoded data is flushed michael@0: * This basically finishes all the decoders from the innermost to the outermost. michael@0: * Finishing an inner decoder may result in data being updated to the outer decoder michael@0: * while we are already in NSS_CMSEncoder_Finish, but that's allright. michael@0: */ michael@0: if (p7ecx->childp7ecx) { michael@0: /* tell the child to start encoding, up to its first data byte, if it michael@0: * hasn't yet */ michael@0: if (!p7ecx->childp7ecx->ecxupdated) { michael@0: p7ecx->childp7ecx->ecxupdated = PR_TRUE; michael@0: rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0); michael@0: if (rv != SECSuccess) { michael@0: NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ michael@0: goto loser; michael@0: } michael@0: } michael@0: rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * On the way back up, there will be no more data (if we had an michael@0: * inner encoder, it is done now!) michael@0: * Flush out any remaining data and/or finish digests. michael@0: */ michael@0: rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: p7ecx->childp7ecx = NULL; michael@0: michael@0: cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); michael@0: if (!cinfo) { michael@0: /* The original programmer didn't expect this to happen */ michael@0: p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); michael@0: SEC_ASN1EncoderClearStreaming(p7ecx->ecx); michael@0: /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ michael@0: rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); michael@0: michael@0: if (p7ecx->error) michael@0: rv = SECFailure; michael@0: michael@0: loser: michael@0: SEC_ASN1EncoderFinish(p7ecx->ecx); michael@0: PORT_Free (p7ecx); michael@0: return rv; michael@0: }