cmsencode.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 741 行 · 第 1/2 页

C
741
字号
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ *  * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. *  * The Original Code is the Netscape security libraries. *  * The Initial Developer of the Original Code is Netscape * Communications Corporation.  Portions created by Netscape are  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All * Rights Reserved. *  * Contributor(s): *  * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable  * instead of those above.  If you wish to allow use of your  * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL.  If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//* * CMS encoding. * * $Id: cmsencode.c,v 1.2 2000/06/13 21:56:29 chrisk%netscape.com Exp $ */#include "cmslocal.h"#include "cert.h"#include "key.h"#include "secasn1.h"#include "secoid.h"#include "secrng.h"#include "secitem.h"#include "pk11func.h"#include "secerr.h"struct nss_cms_encoder_output {    NSSCMSContentCallback outputfn;    void *outputarg;    PLArenaPool *destpoolp;    SECItem *dest;};struct NSSCMSEncoderContextStr {    SEC_ASN1EncoderContext *	ecx;		/* ASN.1 encoder context */    PRBool			ecxupdated;	/* true if data was handed in */    NSSCMSMessage *		cmsg;		/* pointer to the root message */    SECOidTag			type;		/* type tag of the current content */    NSSCMSContent		content;	/* pointer to current content */    struct nss_cms_encoder_output output;	/* output function */    int				error;		/* error code */    NSSCMSEncoderContext *	childp7ecx;	/* link to child encoder context */};static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,			     const unsigned char *data, unsigned long len,			     PRBool final, PRBool innermost);extern const SEC_ASN1Template NSSCMSMessageTemplate[];/* * The little output function that the ASN.1 encoder calls to hand * us bytes which we in turn hand back to our caller (via the callback * they gave us). */static voidnss_cms_encoder_out(void *arg, const char *buf, unsigned long len,		      int depth, SEC_ASN1EncodingPart data_kind){    struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;    unsigned char *dest;    unsigned long offset;#ifdef CMSDEBUG    int i;    fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len);    for (i=0; i < len; i++) {	fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");    }    if ((i % 16) != 0)	fprintf(stderr, "\n");#endif    if (output->outputfn != NULL)	/* call output callback with DER data */	output->outputfn(output->outputarg, buf, len);    if (output->dest != NULL) {	/* store DER data in SECItem */	offset = output->dest->len;	if (offset == 0) {	    dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);	} else {	    dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 				  output->dest->data,				  output->dest->len,				  output->dest->len + len);	}	if (dest == NULL)	    /* oops */	    return;	output->dest->data = dest;	output->dest->len += len;	/* copy it in */	PORT_Memcpy(output->dest->data + offset, buf, len);    }}/* * nss_cms_encoder_notify - ASN.1 encoder callback * * this function is called by the ASN.1 encoder before and after the encoding of * every object. here, it is used to keep track of data structures, set up * encryption and/or digesting and possibly set up child encoders. */static voidnss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth){    NSSCMSEncoderContext *p7ecx;    NSSCMSContentInfo *rootcinfo, *cinfo;    PRBool after = !before;    PLArenaPool *poolp;    SECOidTag childtype;    SECItem *item;    p7ecx = (NSSCMSEncoderContext *)arg;    PORT_Assert(p7ecx != NULL);    rootcinfo = &(p7ecx->cmsg->contentInfo);    poolp = p7ecx->cmsg->poolp;#ifdef CMSDEBUG    fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);#endif    /*     * Watch for the content field, at which point we want to instruct     * the ASN.1 encoder to start taking bytes from the buffer.     */    switch (p7ecx->type) {    default:    case SEC_OID_UNKNOWN:	/* we're still in the root message */	if (after && dest == &(rootcinfo->contentType)) {	    /* got the content type OID now - so find out the type tag */	    p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);	    /* set up a pointer to our current content */	    p7ecx->content = rootcinfo->content;	}	break;    case SEC_OID_PKCS7_DATA:	if (before && dest == &(rootcinfo->rawContent)) {	    /* just set up encoder to grab from user - no encryption or digesting */	    if ((item = rootcinfo->content.data) != NULL)		(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);	    else		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */	}	break;    case SEC_OID_PKCS7_SIGNED_DATA:    case SEC_OID_PKCS7_ENVELOPED_DATA:    case SEC_OID_PKCS7_DIGESTED_DATA:    case SEC_OID_PKCS7_ENCRYPTED_DATA:	/* when we know what the content is, we encode happily until we reach the inner content */	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);	childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);	if (after && dest == &(cinfo->contentType)) {	    /* we're right before encoding the data (if we have some or not) */	    /* (for encrypted data, we're right before the contentEncAlg which may change */	    /*  in nss_cms_before_data because of IV calculation when setting up encryption) */	    if (nss_cms_before_data(p7ecx) != SECSuccess)		p7ecx->error = PORT_GetError();	}	if (before && dest == &(cinfo->rawContent)) {	    if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)		/* we have data - feed it in */		(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);	    else		/* else try to get it from user */		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);	}	if (after && dest == &(cinfo->rawContent)) {	    if (nss_cms_after_data(p7ecx) != SECSuccess)		p7ecx->error = PORT_GetError();	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */	}	break;    }}/* * nss_cms_before_data - setup the current encoder to receive data */static SECStatusnss_cms_before_data(NSSCMSEncoderContext *p7ecx){    SECStatus rv;    SECOidTag childtype;    NSSCMSContentInfo *cinfo;    PLArenaPool *poolp;    NSSCMSEncoderContext *childp7ecx;    const SEC_ASN1Template *template;    poolp = p7ecx->cmsg->poolp;    /* call _Encode_BeforeData handlers */    switch (p7ecx->type) {    case SEC_OID_PKCS7_SIGNED_DATA:	/* we're encoding a signedData, so set up the digests */	rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);	break;    case SEC_OID_PKCS7_DIGESTED_DATA:	/* we're encoding a digestedData, so set up the digest */	rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);	break;    case SEC_OID_PKCS7_ENVELOPED_DATA:	rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);	break;    case SEC_OID_PKCS7_ENCRYPTED_DATA:	rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);	break;    default:	rv = SECFailure;    }    if (rv != SECSuccess)	return SECFailure;    /* ok, now we have a pointer to cinfo */    /* find out what kind of data is encapsulated */        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);    childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);    switch (childtype) {    case SEC_OID_PKCS7_SIGNED_DATA:    case SEC_OID_PKCS7_ENVELOPED_DATA:    case SEC_OID_PKCS7_ENCRYPTED_DATA:    case SEC_OID_PKCS7_DIGESTED_DATA:#if 0    case SEC_OID_PKCS7_DATA:		/* XXX here also??? maybe yes! */#endif	/* in these cases, we need to set up a child encoder! */	/* create new encoder context */	childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));	if (childp7ecx == NULL)	    return SECFailure;	/* the CHILD encoder needs to hand its encoded data to the CURRENT encoder	 * (which will encrypt and/or digest it)	 * this needs to route back into our update function	 * which finds the lowest encoding context & encrypts and computes digests */	childp7ecx->type = childtype;	childp7ecx->content = cinfo->content;	/* use the non-recursive update function here, of course */	childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;	childp7ecx->output.outputarg = p7ecx;	childp7ecx->output.destpoolp = NULL;	childp7ecx->output.dest = NULL;	childp7ecx->cmsg = p7ecx->cmsg;	template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);	if (template == NULL)	    goto loser;		/* cannot happen */	/* now initialize the data for encoding the first third */	switch (childp7ecx->type) {	case SEC_OID_PKCS7_SIGNED_DATA:	    rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);	    break;	case SEC_OID_PKCS7_ENVELOPED_DATA:	    rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);	    break;	case SEC_OID_PKCS7_DIGESTED_DATA:	    rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);	    break;	case SEC_OID_PKCS7_ENCRYPTED_DATA:	    rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);	    break;	case SEC_OID_PKCS7_DATA:	    rv = SECSuccess;	    break;	}	if (rv != SECSuccess)	    goto loser;	/*	 * Initialize the BER encoder.	 */	childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,					   nss_cms_encoder_out, &(childp7ecx->output));	if (childp7ecx->ecx == NULL)	    goto loser;	childp7ecx->ecxupdated = PR_FALSE;	/*	 * Indicate that we are streaming.  We will be streaming until we	 * get past the contents bytes.	 */	SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);	/*	 * The notify function will watch for the contents field.	 */	SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);	/* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */	/* encoding process - we'll do that from the update function instead */	/* otherwise we'd be encoding data from a call of the notify function of the */	/* parent encoder (which would not work) */	/* this will kick off the encoding process & encode everything up to the content bytes,	 * at which point the notify function sets streaming mode (and possibly creates	 * another child encoder). */	if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)	    goto loser;	p7ecx->childp7ecx = childp7ecx;	break;    case SEC_OID_PKCS7_DATA:	p7ecx->childp7ecx = NULL;	break;    default:	/* we do not know this type */	p7ecx->error = SEC_ERROR_BAD_DER;	break;    }    return SECSuccess;loser:    if (childp7ecx) {	if (childp7ecx->ecx)	    SEC_ASN1EncoderFinish(childp7ecx->ecx);	PORT_Free(childp7ecx);    }    return SECFailure;}static SECStatusnss_cms_after_data(NSSCMSEncoderContext *p7ecx){    SECStatus rv;    switch (p7ecx->type) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?