Blame nss/lib/smime/cmsencode.c

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