security/nss/lib/smime/cmsencode.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/smime/cmsencode.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,748 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * CMS encoding.
    1.10 + */
    1.11 +
    1.12 +#include "cmslocal.h"
    1.13 +
    1.14 +#include "cert.h"
    1.15 +#include "key.h"
    1.16 +#include "secasn1.h"
    1.17 +#include "secoid.h"
    1.18 +#include "secitem.h"
    1.19 +#include "pk11func.h"
    1.20 +#include "secerr.h"
    1.21 +
    1.22 +struct nss_cms_encoder_output {
    1.23 +    NSSCMSContentCallback outputfn;
    1.24 +    void *outputarg;
    1.25 +    PLArenaPool *destpoolp;
    1.26 +    SECItem *dest;
    1.27 +};
    1.28 +
    1.29 +struct NSSCMSEncoderContextStr {
    1.30 +    SEC_ASN1EncoderContext *	ecx;		/* ASN.1 encoder context */
    1.31 +    PRBool			ecxupdated;	/* true if data was handed in */
    1.32 +    NSSCMSMessage *		cmsg;		/* pointer to the root message */
    1.33 +    SECOidTag			type;		/* type tag of the current content */
    1.34 +    NSSCMSContent		content;	/* pointer to current content */
    1.35 +    struct nss_cms_encoder_output output;	/* output function */
    1.36 +    int				error;		/* error code */
    1.37 +    NSSCMSEncoderContext *	childp7ecx;	/* link to child encoder context */
    1.38 +};
    1.39 +
    1.40 +static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
    1.41 +static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
    1.42 +static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
    1.43 +static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
    1.44 +			     const unsigned char *data, unsigned long len,
    1.45 +			     PRBool final, PRBool innermost);
    1.46 +
    1.47 +extern const SEC_ASN1Template NSSCMSMessageTemplate[];
    1.48 +
    1.49 +/*
    1.50 + * The little output function that the ASN.1 encoder calls to hand
    1.51 + * us bytes which we in turn hand back to our caller (via the callback
    1.52 + * they gave us).
    1.53 + */
    1.54 +static void
    1.55 +nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
    1.56 +		      int depth, SEC_ASN1EncodingPart data_kind)
    1.57 +{
    1.58 +    struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
    1.59 +    unsigned char *dest;
    1.60 +    unsigned long offset;
    1.61 +
    1.62 +#ifdef CMSDEBUG
    1.63 +    int i;
    1.64 +    const char *data_name = "unknown";
    1.65 +
    1.66 +    switch (data_kind) {
    1.67 +    case SEC_ASN1_Identifier:
    1.68 +        data_name = "identifier";
    1.69 +        break;
    1.70 +    case SEC_ASN1_Length:
    1.71 +        data_name = "length";
    1.72 +        break;
    1.73 +    case SEC_ASN1_Contents:
    1.74 +        data_name = "contents";
    1.75 +        break;
    1.76 +    case SEC_ASN1_EndOfContents:
    1.77 +        data_name = "end-of-contents";
    1.78 +        break;
    1.79 +    }
    1.80 +    fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len);
    1.81 +    for (i=0; i < len; i++) {
    1.82 +	fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
    1.83 +    }
    1.84 +    if ((i % 16) != 0)
    1.85 +	fprintf(stderr, "\n");
    1.86 +#endif
    1.87 +
    1.88 +    if (output->outputfn != NULL)
    1.89 +	/* call output callback with DER data */
    1.90 +	output->outputfn(output->outputarg, buf, len);
    1.91 +
    1.92 +    if (output->dest != NULL) {
    1.93 +	/* store DER data in SECItem */
    1.94 +	offset = output->dest->len;
    1.95 +	if (offset == 0) {
    1.96 +	    dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
    1.97 +	} else {
    1.98 +	    dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 
    1.99 +				  output->dest->data,
   1.100 +				  output->dest->len,
   1.101 +				  output->dest->len + len);
   1.102 +	}
   1.103 +	if (dest == NULL)
   1.104 +	    /* oops */
   1.105 +	    return;
   1.106 +
   1.107 +	output->dest->data = dest;
   1.108 +	output->dest->len += len;
   1.109 +
   1.110 +	/* copy it in */
   1.111 +	PORT_Memcpy(output->dest->data + offset, buf, len);
   1.112 +    }
   1.113 +}
   1.114 +
   1.115 +/*
   1.116 + * nss_cms_encoder_notify - ASN.1 encoder callback
   1.117 + *
   1.118 + * this function is called by the ASN.1 encoder before and after the encoding of
   1.119 + * every object. here, it is used to keep track of data structures, set up
   1.120 + * encryption and/or digesting and possibly set up child encoders.
   1.121 + */
   1.122 +static void
   1.123 +nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
   1.124 +{
   1.125 +    NSSCMSEncoderContext *p7ecx;
   1.126 +    NSSCMSContentInfo *rootcinfo, *cinfo;
   1.127 +    PRBool after = !before;
   1.128 +    PLArenaPool *poolp;
   1.129 +    SECOidTag childtype;
   1.130 +    SECItem *item;
   1.131 +
   1.132 +    p7ecx = (NSSCMSEncoderContext *)arg;
   1.133 +    PORT_Assert(p7ecx != NULL);
   1.134 +
   1.135 +    rootcinfo = &(p7ecx->cmsg->contentInfo);
   1.136 +    poolp = p7ecx->cmsg->poolp;
   1.137 +
   1.138 +#ifdef CMSDEBUG
   1.139 +    fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
   1.140 +#endif
   1.141 +
   1.142 +    /*
   1.143 +     * Watch for the content field, at which point we want to instruct
   1.144 +     * the ASN.1 encoder to start taking bytes from the buffer.
   1.145 +     */
   1.146 +    if (NSS_CMSType_IsData(p7ecx->type)) {
   1.147 +	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.148 +	if (before && dest == &(cinfo->rawContent)) {
   1.149 +	    /* just set up encoder to grab from user - no encryption or digesting */
   1.150 +	    if ((item = cinfo->content.data) != NULL)
   1.151 +		(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
   1.152 +	    else
   1.153 +		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   1.154 +	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */
   1.155 +	}
   1.156 +    } else if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   1.157 +	/* when we know what the content is, we encode happily until we reach the inner content */
   1.158 +	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.159 +	childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   1.160 +
   1.161 +	if (after && dest == &(cinfo->contentType)) {
   1.162 +	    /* we're right before encoding the data (if we have some or not) */
   1.163 +	    /* (for encrypted data, we're right before the contentEncAlg which may change */
   1.164 +	    /*  in nss_cms_before_data because of IV calculation when setting up encryption) */
   1.165 +	    if (nss_cms_before_data(p7ecx) != SECSuccess)
   1.166 +		p7ecx->error = PORT_GetError();
   1.167 +	}
   1.168 +	if (before && dest == &(cinfo->rawContent)) {
   1.169 +	    if (p7ecx->childp7ecx == NULL) {
   1.170 +		if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) {
   1.171 +		    /* we are the innermost non-data and we have data - feed it in */
   1.172 +		    (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
   1.173 +	        } else {
   1.174 +		    /* else we'll have to get data from user */
   1.175 +		    SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   1.176 +		}
   1.177 +	    } else {
   1.178 +	        /* if we have a nested encoder, wait for its data */
   1.179 +		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   1.180 +	    }
   1.181 +	}
   1.182 +	if (after && dest == &(cinfo->rawContent)) {
   1.183 +	    if (nss_cms_after_data(p7ecx) != SECSuccess)
   1.184 +		p7ecx->error = PORT_GetError();
   1.185 +	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */
   1.186 +	}
   1.187 +    } else {
   1.188 +	/* we're still in the root message */
   1.189 +	if (after && dest == &(rootcinfo->contentType)) {
   1.190 +	    /* got the content type OID now - so find out the type tag */
   1.191 +	    p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
   1.192 +	    /* set up a pointer to our current content */
   1.193 +	    p7ecx->content = rootcinfo->content;
   1.194 +	}
   1.195 +    }
   1.196 +}
   1.197 +
   1.198 +/*
   1.199 + * nss_cms_before_data - setup the current encoder to receive data
   1.200 + */
   1.201 +static SECStatus
   1.202 +nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
   1.203 +{
   1.204 +    SECStatus rv;
   1.205 +    SECOidTag childtype;
   1.206 +    NSSCMSContentInfo *cinfo;
   1.207 +    PLArenaPool *poolp;
   1.208 +    NSSCMSEncoderContext *childp7ecx;
   1.209 +    const SEC_ASN1Template *template;
   1.210 +
   1.211 +    poolp = p7ecx->cmsg->poolp;
   1.212 +
   1.213 +    /* call _Encode_BeforeData handlers */
   1.214 +    switch (p7ecx->type) {
   1.215 +    case SEC_OID_PKCS7_SIGNED_DATA:
   1.216 +	/* we're encoding a signedData, so set up the digests */
   1.217 +	rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
   1.218 +	break;
   1.219 +    case SEC_OID_PKCS7_DIGESTED_DATA:
   1.220 +	/* we're encoding a digestedData, so set up the digest */
   1.221 +	rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
   1.222 +	break;
   1.223 +    case SEC_OID_PKCS7_ENVELOPED_DATA:
   1.224 +	rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
   1.225 +	break;
   1.226 +    case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1.227 +	rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
   1.228 +	break;
   1.229 +    default:
   1.230 +        if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   1.231 +	    rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, p7ecx->content.genericData);
   1.232 +	} else {
   1.233 +	    rv = SECFailure;
   1.234 +	}
   1.235 +    }
   1.236 +    if (rv != SECSuccess)
   1.237 +	return SECFailure;
   1.238 +
   1.239 +    /* ok, now we have a pointer to cinfo */
   1.240 +    /* find out what kind of data is encapsulated */
   1.241 +    
   1.242 +    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.243 +    childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   1.244 +
   1.245 +    if (NSS_CMSType_IsWrapper(childtype)) {
   1.246 +	/* in these cases, we need to set up a child encoder! */
   1.247 +	/* create new encoder context */
   1.248 +	childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
   1.249 +	if (childp7ecx == NULL)
   1.250 +	    return SECFailure;
   1.251 +
   1.252 +	/* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
   1.253 +	 * (which will encrypt and/or digest it)
   1.254 +	 * this needs to route back into our update function
   1.255 +	 * which finds the lowest encoding context & encrypts and computes digests */
   1.256 +	childp7ecx->type = childtype;
   1.257 +	childp7ecx->content = cinfo->content;
   1.258 +	/* use the non-recursive update function here, of course */
   1.259 +	childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
   1.260 +	childp7ecx->output.outputarg = p7ecx;
   1.261 +	childp7ecx->output.destpoolp = NULL;
   1.262 +	childp7ecx->output.dest = NULL;
   1.263 +	childp7ecx->cmsg = p7ecx->cmsg;
   1.264 +	childp7ecx->ecxupdated = PR_FALSE;
   1.265 +	childp7ecx->childp7ecx = NULL;
   1.266 +
   1.267 +	template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
   1.268 +	if (template == NULL)
   1.269 +	    goto loser;		/* cannot happen */
   1.270 +
   1.271 +	/* now initialize the data for encoding the first third */
   1.272 +	switch (childp7ecx->type) {
   1.273 +	case SEC_OID_PKCS7_SIGNED_DATA:
   1.274 +	    rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
   1.275 +	    break;
   1.276 +	case SEC_OID_PKCS7_ENVELOPED_DATA:
   1.277 +	    rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
   1.278 +	    break;
   1.279 +	case SEC_OID_PKCS7_DIGESTED_DATA:
   1.280 +	    rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
   1.281 +	    break;
   1.282 +	case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1.283 +	    rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
   1.284 +	    break;
   1.285 +	default:
   1.286 +	    rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, cinfo->content.genericData);
   1.287 +	    break;
   1.288 +	}
   1.289 +	if (rv != SECSuccess)
   1.290 +	    goto loser;
   1.291 +
   1.292 +	/*
   1.293 +	 * Initialize the BER encoder.
   1.294 +	 */
   1.295 +	childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
   1.296 +					   nss_cms_encoder_out, &(childp7ecx->output));
   1.297 +	if (childp7ecx->ecx == NULL)
   1.298 +	    goto loser;
   1.299 +
   1.300 +	/*
   1.301 +	 * Indicate that we are streaming.  We will be streaming until we
   1.302 +	 * get past the contents bytes.
   1.303 +	 */
   1.304 +        if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
   1.305 +	    SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
   1.306 +
   1.307 +	/*
   1.308 +	 * The notify function will watch for the contents field.
   1.309 +	 */
   1.310 +	p7ecx->childp7ecx = childp7ecx;
   1.311 +	SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
   1.312 +
   1.313 +	/* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
   1.314 +	/* encoding process - we'll do that from the update function instead */
   1.315 +	/* otherwise we'd be encoding data from a call of the notify function of the */
   1.316 +	/* parent encoder (which would not work) */
   1.317 +
   1.318 +    } else if (NSS_CMSType_IsData(childtype)) {
   1.319 +	p7ecx->childp7ecx = NULL;
   1.320 +    } else {
   1.321 +	/* we do not know this type */
   1.322 +	p7ecx->error = SEC_ERROR_BAD_DER;
   1.323 +    }
   1.324 +
   1.325 +    return SECSuccess;
   1.326 +
   1.327 +loser:
   1.328 +    if (childp7ecx) {
   1.329 +	if (childp7ecx->ecx)
   1.330 +	    SEC_ASN1EncoderFinish(childp7ecx->ecx);
   1.331 +	PORT_Free(childp7ecx);
   1.332 +	p7ecx->childp7ecx = NULL;
   1.333 +    }
   1.334 +    return SECFailure;
   1.335 +}
   1.336 +
   1.337 +static SECStatus
   1.338 +nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
   1.339 +{
   1.340 +    SECStatus rv = SECFailure;
   1.341 +
   1.342 +    switch (p7ecx->type) {
   1.343 +    case SEC_OID_PKCS7_SIGNED_DATA:
   1.344 +	/* this will finish the digests and sign */
   1.345 +	rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
   1.346 +	break;
   1.347 +    case SEC_OID_PKCS7_ENVELOPED_DATA:
   1.348 +	rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
   1.349 +	break;
   1.350 +    case SEC_OID_PKCS7_DIGESTED_DATA:
   1.351 +	rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
   1.352 +	break;
   1.353 +    case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1.354 +	rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
   1.355 +	break;
   1.356 +    default:
   1.357 +        if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   1.358 +	    rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, p7ecx->content.genericData);
   1.359 +	} else {
   1.360 +	    rv = SECFailure;
   1.361 +	}
   1.362 +	break;
   1.363 +    }
   1.364 +    return rv;
   1.365 +}
   1.366 +
   1.367 +/*
   1.368 + * nss_cms_encoder_work_data - process incoming data
   1.369 + *
   1.370 + * (from the user or the next encoding layer)
   1.371 + * Here, we need to digest and/or encrypt, then pass it on
   1.372 + */
   1.373 +static SECStatus
   1.374 +nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
   1.375 +			     const unsigned char *data, unsigned long len,
   1.376 +			     PRBool final, PRBool innermost)
   1.377 +{
   1.378 +    unsigned char *buf = NULL;
   1.379 +    SECStatus rv;
   1.380 +    NSSCMSContentInfo *cinfo;
   1.381 +
   1.382 +    rv = SECSuccess;		/* may as well be optimistic */
   1.383 +
   1.384 +    /*
   1.385 +     * We should really have data to process, or we should be trying
   1.386 +     * to finish/flush the last block.  (This is an overly paranoid
   1.387 +     * check since all callers are in this file and simple inspection
   1.388 +     * proves they do it right.  But it could find a bug in future
   1.389 +     * modifications/development, that is why it is here.)
   1.390 +     */
   1.391 +    PORT_Assert ((data != NULL && len) || final);
   1.392 +
   1.393 +    /* we got data (either from the caller, or from a lower level encoder) */
   1.394 +    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.395 +    if (!cinfo) {
   1.396 +	/* The original programmer didn't expect this to happen */
   1.397 +	p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   1.398 +	return SECFailure;
   1.399 +    }
   1.400 +
   1.401 +    /* Update the running digest. */
   1.402 +    if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL)
   1.403 +	NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
   1.404 +
   1.405 +    /* Encrypt this chunk. */
   1.406 +    if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
   1.407 +	unsigned int inlen;	/* length of data being encrypted */
   1.408 +	unsigned int outlen;	/* length of encrypted data */
   1.409 +	unsigned int buflen;	/* length available for encrypted data */
   1.410 +
   1.411 +	inlen = len;
   1.412 +	buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, inlen, final);
   1.413 +	if (buflen == 0) {
   1.414 +	    /*
   1.415 +	     * No output is expected, but the input data may be buffered
   1.416 +	     * so we still have to call Encrypt.
   1.417 +	     */
   1.418 +	    rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0,
   1.419 +				   data, inlen, final);
   1.420 +	    if (final) {
   1.421 +		len = 0;
   1.422 +		goto done;
   1.423 +	    }
   1.424 +	    return rv;
   1.425 +	}
   1.426 +
   1.427 +	if (dest != NULL)
   1.428 +	    buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
   1.429 +	else
   1.430 +	    buf = (unsigned char*)PORT_Alloc(buflen);
   1.431 +
   1.432 +	if (buf == NULL) {
   1.433 +	    rv = SECFailure;
   1.434 +	} else {
   1.435 +	    rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
   1.436 +				   data, inlen, final);
   1.437 +	    data = buf;
   1.438 +	    len = outlen;
   1.439 +	}
   1.440 +	if (rv != SECSuccess)
   1.441 +	    /* encryption or malloc failed? */
   1.442 +	    return rv;
   1.443 +    }
   1.444 +
   1.445 +
   1.446 +    /*
   1.447 +     * at this point (data,len) has everything we'd like to give to the CURRENT encoder
   1.448 +     * (which will encode it, then hand it back to the user or the parent encoder)
   1.449 +     * We don't encode the data if we're innermost and we're told not to include the data
   1.450 +     */
   1.451 +    if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != cinfo->content.pointer))
   1.452 +	rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
   1.453 +
   1.454 +done:
   1.455 +
   1.456 +    if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
   1.457 +	if (dest != NULL) {
   1.458 +	    dest->data = buf;
   1.459 +	    dest->len = len;
   1.460 +	} else if (buf != NULL) {
   1.461 +	    PORT_Free (buf);
   1.462 +	}
   1.463 +    }
   1.464 +    return rv;
   1.465 +}
   1.466 +
   1.467 +/*
   1.468 + * nss_cms_encoder_update - deliver encoded data to the next higher level
   1.469 + *
   1.470 + * no recursion here because we REALLY want to end up at the next higher encoder!
   1.471 + */
   1.472 +static SECStatus
   1.473 +nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
   1.474 +{
   1.475 +    /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
   1.476 +    return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
   1.477 +}
   1.478 +
   1.479 +/*
   1.480 + * NSS_CMSEncoder_Start - set up encoding of a CMS message
   1.481 + *
   1.482 + * "cmsg" - message to encode
   1.483 + * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
   1.484 + *                           will not be called if NULL.
   1.485 + * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
   1.486 + * "destpoolp" - pool to allocate DER-encoded output in
   1.487 + * "pwfn", pwfn_arg" - callback function for getting token password
   1.488 + * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
   1.489 + * "detached_digestalgs", "detached_digests" - digests from detached content
   1.490 + */
   1.491 +NSSCMSEncoderContext *
   1.492 +NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
   1.493 +			NSSCMSContentCallback outputfn, void *outputarg,
   1.494 +			SECItem *dest, PLArenaPool *destpoolp,
   1.495 +			PK11PasswordFunc pwfn, void *pwfn_arg,
   1.496 +			NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
   1.497 +			SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
   1.498 +{
   1.499 +    NSSCMSEncoderContext *p7ecx;
   1.500 +    SECStatus rv;
   1.501 +    NSSCMSContentInfo *cinfo;
   1.502 +    SECOidTag tag;
   1.503 +
   1.504 +    NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
   1.505 +					detached_digestalgs, detached_digests);
   1.506 +
   1.507 +    p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
   1.508 +    if (p7ecx == NULL) {
   1.509 +	PORT_SetError(SEC_ERROR_NO_MEMORY);
   1.510 +	return NULL;
   1.511 +    }
   1.512 +
   1.513 +    p7ecx->cmsg = cmsg;
   1.514 +    p7ecx->output.outputfn = outputfn;
   1.515 +    p7ecx->output.outputarg = outputarg;
   1.516 +    p7ecx->output.dest = dest;
   1.517 +    p7ecx->output.destpoolp = destpoolp;
   1.518 +    p7ecx->type = SEC_OID_UNKNOWN;
   1.519 +
   1.520 +    cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
   1.521 +
   1.522 +    tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   1.523 +    switch (tag) {
   1.524 +    case SEC_OID_PKCS7_SIGNED_DATA:
   1.525 +	rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
   1.526 +	break;
   1.527 +    case SEC_OID_PKCS7_ENVELOPED_DATA:
   1.528 +	rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
   1.529 +	break;
   1.530 +    case SEC_OID_PKCS7_DIGESTED_DATA:
   1.531 +	rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
   1.532 +	break;
   1.533 +    case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1.534 +	rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
   1.535 +	break;
   1.536 +    default:
   1.537 +        if (NSS_CMSType_IsWrapper(tag)) {
   1.538 +	    rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, 
   1.539 +						p7ecx->content.genericData);
   1.540 +	} else {
   1.541 +	    rv = SECFailure;
   1.542 +	}
   1.543 +	break;
   1.544 +    }
   1.545 +    if (rv != SECSuccess) {
   1.546 +	PORT_Free(p7ecx);
   1.547 +	return NULL;
   1.548 +    }
   1.549 +
   1.550 +    /* Initialize the BER encoder.
   1.551 +     * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
   1.552 +    p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
   1.553 +				       nss_cms_encoder_out, &(p7ecx->output));
   1.554 +    if (p7ecx->ecx == NULL) {
   1.555 +	PORT_Free (p7ecx);
   1.556 +	return NULL;
   1.557 +    }
   1.558 +    p7ecx->ecxupdated = PR_FALSE;
   1.559 +
   1.560 +    /*
   1.561 +     * Indicate that we are streaming.  We will be streaming until we
   1.562 +     * get past the contents bytes.
   1.563 +     */
   1.564 +    if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
   1.565 +	SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
   1.566 +
   1.567 +    /*
   1.568 +     * The notify function will watch for the contents field.
   1.569 +     */
   1.570 +    SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
   1.571 +
   1.572 +    /* this will kick off the encoding process & encode everything up to the content bytes,
   1.573 +     * at which point the notify function sets streaming mode (and possibly creates
   1.574 +     * a child encoder). */
   1.575 +    p7ecx->ecxupdated = PR_TRUE;
   1.576 +    if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
   1.577 +	PORT_Free (p7ecx);
   1.578 +	return NULL;
   1.579 +    }
   1.580 +
   1.581 +    return p7ecx;
   1.582 +}
   1.583 +
   1.584 +/*
   1.585 + * NSS_CMSEncoder_Update - take content data delivery from the user
   1.586 + *
   1.587 + * "p7ecx" - encoder context
   1.588 + * "data" - content data
   1.589 + * "len" - length of content data
   1.590 + *
   1.591 + * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
   1.592 + * then hand the data to the work_data fn
   1.593 + */
   1.594 +SECStatus
   1.595 +NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
   1.596 +{
   1.597 +    SECStatus rv;
   1.598 +    NSSCMSContentInfo *cinfo;
   1.599 +    SECOidTag childtype;
   1.600 +
   1.601 +    if (p7ecx->error)
   1.602 +	return SECFailure;
   1.603 +
   1.604 +    /* hand data to the innermost decoder */
   1.605 +    if (p7ecx->childp7ecx) {
   1.606 +	/* tell the child to start encoding, up to its first data byte, if it
   1.607 +	 * hasn't started yet */
   1.608 +	if (!p7ecx->childp7ecx->ecxupdated) {
   1.609 +	    p7ecx->childp7ecx->ecxupdated = PR_TRUE;
   1.610 +	    if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess)
   1.611 +	        return SECFailure;
   1.612 +	}
   1.613 +	/* recursion here */
   1.614 +	rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
   1.615 +    } else {
   1.616 +	/* we are at innermost decoder */
   1.617 +	/* find out about our inner content type - must be data */
   1.618 +	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.619 +	if (!cinfo) {
   1.620 +	    /* The original programmer didn't expect this to happen */
   1.621 +	    p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   1.622 +	    return SECFailure;
   1.623 +	}
   1.624 +
   1.625 +	childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   1.626 +	if (!NSS_CMSType_IsData(childtype))
   1.627 +	    return SECFailure;
   1.628 +	/* and we must not have preset data */
   1.629 +	if (cinfo->content.data != NULL)
   1.630 +	    return SECFailure;
   1.631 +
   1.632 +	/*  hand it the data so it can encode it (let DER trickle up the chain) */
   1.633 +	rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
   1.634 +    }
   1.635 +    return rv;
   1.636 +}
   1.637 +
   1.638 +/*
   1.639 + * NSS_CMSEncoder_Cancel - stop all encoding
   1.640 + *
   1.641 + * we need to walk down the chain of encoders and the finish them from the innermost out
   1.642 + */
   1.643 +SECStatus
   1.644 +NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
   1.645 +{
   1.646 +    SECStatus rv = SECFailure;
   1.647 +
   1.648 +    /* XXX do this right! */
   1.649 +
   1.650 +    /*
   1.651 +     * Finish any inner decoders before us so that all the encoded data is flushed
   1.652 +     * This basically finishes all the decoders from the innermost to the outermost.
   1.653 +     * Finishing an inner decoder may result in data being updated to the outer decoder
   1.654 +     * while we are already in NSS_CMSEncoder_Finish, but that's allright.
   1.655 +     */
   1.656 +    if (p7ecx->childp7ecx) {
   1.657 +	rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   1.658 +	/* remember rv for now */
   1.659 +    }
   1.660 +
   1.661 +    /*
   1.662 +     * On the way back up, there will be no more data (if we had an
   1.663 +     * inner encoder, it is done now!)
   1.664 +     * Flush out any remaining data and/or finish digests.
   1.665 +     */
   1.666 +    rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
   1.667 +    if (rv != SECSuccess)
   1.668 +	goto loser;
   1.669 +
   1.670 +    p7ecx->childp7ecx = NULL;
   1.671 +
   1.672 +    /* kick the encoder back into working mode again.
   1.673 +     * We turn off streaming stuff (which will cause the encoder to continue
   1.674 +     * encoding happily, now that we have all the data (like digests) ready for it).
   1.675 +     */
   1.676 +    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
   1.677 +    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
   1.678 +
   1.679 +    /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
   1.680 +    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
   1.681 +
   1.682 +loser:
   1.683 +    SEC_ASN1EncoderFinish(p7ecx->ecx);
   1.684 +    PORT_Free (p7ecx);
   1.685 +    return rv;
   1.686 +}
   1.687 +
   1.688 +/*
   1.689 + * NSS_CMSEncoder_Finish - signal the end of data
   1.690 + *
   1.691 + * we need to walk down the chain of encoders and the finish them from the innermost out
   1.692 + */
   1.693 +SECStatus
   1.694 +NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
   1.695 +{
   1.696 +    SECStatus rv = SECFailure;
   1.697 +    NSSCMSContentInfo *cinfo;
   1.698 +
   1.699 +    /*
   1.700 +     * Finish any inner decoders before us so that all the encoded data is flushed
   1.701 +     * This basically finishes all the decoders from the innermost to the outermost.
   1.702 +     * Finishing an inner decoder may result in data being updated to the outer decoder
   1.703 +     * while we are already in NSS_CMSEncoder_Finish, but that's allright.
   1.704 +     */
   1.705 +    if (p7ecx->childp7ecx) {
   1.706 +	/* tell the child to start encoding, up to its first data byte, if it
   1.707 +	 * hasn't yet */
   1.708 +	if (!p7ecx->childp7ecx->ecxupdated) {
   1.709 +	    p7ecx->childp7ecx->ecxupdated = PR_TRUE;
   1.710 +	    rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0);
   1.711 +	    if (rv != SECSuccess) {
   1.712 +		NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   1.713 +		goto loser;
   1.714 +	    }
   1.715 +	}
   1.716 +	rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   1.717 +	if (rv != SECSuccess)
   1.718 +	    goto loser;
   1.719 +    }
   1.720 +
   1.721 +    /*
   1.722 +     * On the way back up, there will be no more data (if we had an
   1.723 +     * inner encoder, it is done now!)
   1.724 +     * Flush out any remaining data and/or finish digests.
   1.725 +     */
   1.726 +    rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
   1.727 +    if (rv != SECSuccess)
   1.728 +	goto loser;
   1.729 +
   1.730 +    p7ecx->childp7ecx = NULL;
   1.731 +
   1.732 +    cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   1.733 +    if (!cinfo) {
   1.734 +	/* The original programmer didn't expect this to happen */
   1.735 +	p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   1.736 +	rv = SECFailure;
   1.737 +	goto loser;
   1.738 +    }
   1.739 +    SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
   1.740 +    SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
   1.741 +    /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
   1.742 +    rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
   1.743 +
   1.744 +    if (p7ecx->error)
   1.745 +	rv = SECFailure;
   1.746 +
   1.747 +loser:
   1.748 +    SEC_ASN1EncoderFinish(p7ecx->ecx);
   1.749 +    PORT_Free (p7ecx);
   1.750 +    return rv;
   1.751 +}

mercurial