security/nss/lib/smime/cmsencode.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6  * CMS encoding.
     7  */
     9 #include "cmslocal.h"
    11 #include "cert.h"
    12 #include "key.h"
    13 #include "secasn1.h"
    14 #include "secoid.h"
    15 #include "secitem.h"
    16 #include "pk11func.h"
    17 #include "secerr.h"
    19 struct nss_cms_encoder_output {
    20     NSSCMSContentCallback outputfn;
    21     void *outputarg;
    22     PLArenaPool *destpoolp;
    23     SECItem *dest;
    24 };
    26 struct NSSCMSEncoderContextStr {
    27     SEC_ASN1EncoderContext *	ecx;		/* ASN.1 encoder context */
    28     PRBool			ecxupdated;	/* true if data was handed in */
    29     NSSCMSMessage *		cmsg;		/* pointer to the root message */
    30     SECOidTag			type;		/* type tag of the current content */
    31     NSSCMSContent		content;	/* pointer to current content */
    32     struct nss_cms_encoder_output output;	/* output function */
    33     int				error;		/* error code */
    34     NSSCMSEncoderContext *	childp7ecx;	/* link to child encoder context */
    35 };
    37 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
    38 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
    39 static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
    40 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
    41 			     const unsigned char *data, unsigned long len,
    42 			     PRBool final, PRBool innermost);
    44 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
    46 /*
    47  * The little output function that the ASN.1 encoder calls to hand
    48  * us bytes which we in turn hand back to our caller (via the callback
    49  * they gave us).
    50  */
    51 static void
    52 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
    53 		      int depth, SEC_ASN1EncodingPart data_kind)
    54 {
    55     struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
    56     unsigned char *dest;
    57     unsigned long offset;
    59 #ifdef CMSDEBUG
    60     int i;
    61     const char *data_name = "unknown";
    63     switch (data_kind) {
    64     case SEC_ASN1_Identifier:
    65         data_name = "identifier";
    66         break;
    67     case SEC_ASN1_Length:
    68         data_name = "length";
    69         break;
    70     case SEC_ASN1_Contents:
    71         data_name = "contents";
    72         break;
    73     case SEC_ASN1_EndOfContents:
    74         data_name = "end-of-contents";
    75         break;
    76     }
    77     fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len);
    78     for (i=0; i < len; i++) {
    79 	fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
    80     }
    81     if ((i % 16) != 0)
    82 	fprintf(stderr, "\n");
    83 #endif
    85     if (output->outputfn != NULL)
    86 	/* call output callback with DER data */
    87 	output->outputfn(output->outputarg, buf, len);
    89     if (output->dest != NULL) {
    90 	/* store DER data in SECItem */
    91 	offset = output->dest->len;
    92 	if (offset == 0) {
    93 	    dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
    94 	} else {
    95 	    dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 
    96 				  output->dest->data,
    97 				  output->dest->len,
    98 				  output->dest->len + len);
    99 	}
   100 	if (dest == NULL)
   101 	    /* oops */
   102 	    return;
   104 	output->dest->data = dest;
   105 	output->dest->len += len;
   107 	/* copy it in */
   108 	PORT_Memcpy(output->dest->data + offset, buf, len);
   109     }
   110 }
   112 /*
   113  * nss_cms_encoder_notify - ASN.1 encoder callback
   114  *
   115  * this function is called by the ASN.1 encoder before and after the encoding of
   116  * every object. here, it is used to keep track of data structures, set up
   117  * encryption and/or digesting and possibly set up child encoders.
   118  */
   119 static void
   120 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
   121 {
   122     NSSCMSEncoderContext *p7ecx;
   123     NSSCMSContentInfo *rootcinfo, *cinfo;
   124     PRBool after = !before;
   125     PLArenaPool *poolp;
   126     SECOidTag childtype;
   127     SECItem *item;
   129     p7ecx = (NSSCMSEncoderContext *)arg;
   130     PORT_Assert(p7ecx != NULL);
   132     rootcinfo = &(p7ecx->cmsg->contentInfo);
   133     poolp = p7ecx->cmsg->poolp;
   135 #ifdef CMSDEBUG
   136     fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
   137 #endif
   139     /*
   140      * Watch for the content field, at which point we want to instruct
   141      * the ASN.1 encoder to start taking bytes from the buffer.
   142      */
   143     if (NSS_CMSType_IsData(p7ecx->type)) {
   144 	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   145 	if (before && dest == &(cinfo->rawContent)) {
   146 	    /* just set up encoder to grab from user - no encryption or digesting */
   147 	    if ((item = cinfo->content.data) != NULL)
   148 		(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
   149 	    else
   150 		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   151 	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */
   152 	}
   153     } else if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   154 	/* when we know what the content is, we encode happily until we reach the inner content */
   155 	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   156 	childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   158 	if (after && dest == &(cinfo->contentType)) {
   159 	    /* we're right before encoding the data (if we have some or not) */
   160 	    /* (for encrypted data, we're right before the contentEncAlg which may change */
   161 	    /*  in nss_cms_before_data because of IV calculation when setting up encryption) */
   162 	    if (nss_cms_before_data(p7ecx) != SECSuccess)
   163 		p7ecx->error = PORT_GetError();
   164 	}
   165 	if (before && dest == &(cinfo->rawContent)) {
   166 	    if (p7ecx->childp7ecx == NULL) {
   167 		if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) {
   168 		    /* we are the innermost non-data and we have data - feed it in */
   169 		    (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
   170 	        } else {
   171 		    /* else we'll have to get data from user */
   172 		    SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   173 		}
   174 	    } else {
   175 	        /* if we have a nested encoder, wait for its data */
   176 		SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
   177 	    }
   178 	}
   179 	if (after && dest == &(cinfo->rawContent)) {
   180 	    if (nss_cms_after_data(p7ecx) != SECSuccess)
   181 		p7ecx->error = PORT_GetError();
   182 	    SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);	/* no need to get notified anymore */
   183 	}
   184     } else {
   185 	/* we're still in the root message */
   186 	if (after && dest == &(rootcinfo->contentType)) {
   187 	    /* got the content type OID now - so find out the type tag */
   188 	    p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
   189 	    /* set up a pointer to our current content */
   190 	    p7ecx->content = rootcinfo->content;
   191 	}
   192     }
   193 }
   195 /*
   196  * nss_cms_before_data - setup the current encoder to receive data
   197  */
   198 static SECStatus
   199 nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
   200 {
   201     SECStatus rv;
   202     SECOidTag childtype;
   203     NSSCMSContentInfo *cinfo;
   204     PLArenaPool *poolp;
   205     NSSCMSEncoderContext *childp7ecx;
   206     const SEC_ASN1Template *template;
   208     poolp = p7ecx->cmsg->poolp;
   210     /* call _Encode_BeforeData handlers */
   211     switch (p7ecx->type) {
   212     case SEC_OID_PKCS7_SIGNED_DATA:
   213 	/* we're encoding a signedData, so set up the digests */
   214 	rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
   215 	break;
   216     case SEC_OID_PKCS7_DIGESTED_DATA:
   217 	/* we're encoding a digestedData, so set up the digest */
   218 	rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
   219 	break;
   220     case SEC_OID_PKCS7_ENVELOPED_DATA:
   221 	rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
   222 	break;
   223     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   224 	rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
   225 	break;
   226     default:
   227         if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   228 	    rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, p7ecx->content.genericData);
   229 	} else {
   230 	    rv = SECFailure;
   231 	}
   232     }
   233     if (rv != SECSuccess)
   234 	return SECFailure;
   236     /* ok, now we have a pointer to cinfo */
   237     /* find out what kind of data is encapsulated */
   239     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   240     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   242     if (NSS_CMSType_IsWrapper(childtype)) {
   243 	/* in these cases, we need to set up a child encoder! */
   244 	/* create new encoder context */
   245 	childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
   246 	if (childp7ecx == NULL)
   247 	    return SECFailure;
   249 	/* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
   250 	 * (which will encrypt and/or digest it)
   251 	 * this needs to route back into our update function
   252 	 * which finds the lowest encoding context & encrypts and computes digests */
   253 	childp7ecx->type = childtype;
   254 	childp7ecx->content = cinfo->content;
   255 	/* use the non-recursive update function here, of course */
   256 	childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
   257 	childp7ecx->output.outputarg = p7ecx;
   258 	childp7ecx->output.destpoolp = NULL;
   259 	childp7ecx->output.dest = NULL;
   260 	childp7ecx->cmsg = p7ecx->cmsg;
   261 	childp7ecx->ecxupdated = PR_FALSE;
   262 	childp7ecx->childp7ecx = NULL;
   264 	template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
   265 	if (template == NULL)
   266 	    goto loser;		/* cannot happen */
   268 	/* now initialize the data for encoding the first third */
   269 	switch (childp7ecx->type) {
   270 	case SEC_OID_PKCS7_SIGNED_DATA:
   271 	    rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
   272 	    break;
   273 	case SEC_OID_PKCS7_ENVELOPED_DATA:
   274 	    rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
   275 	    break;
   276 	case SEC_OID_PKCS7_DIGESTED_DATA:
   277 	    rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
   278 	    break;
   279 	case SEC_OID_PKCS7_ENCRYPTED_DATA:
   280 	    rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
   281 	    break;
   282 	default:
   283 	    rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, cinfo->content.genericData);
   284 	    break;
   285 	}
   286 	if (rv != SECSuccess)
   287 	    goto loser;
   289 	/*
   290 	 * Initialize the BER encoder.
   291 	 */
   292 	childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
   293 					   nss_cms_encoder_out, &(childp7ecx->output));
   294 	if (childp7ecx->ecx == NULL)
   295 	    goto loser;
   297 	/*
   298 	 * Indicate that we are streaming.  We will be streaming until we
   299 	 * get past the contents bytes.
   300 	 */
   301         if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
   302 	    SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
   304 	/*
   305 	 * The notify function will watch for the contents field.
   306 	 */
   307 	p7ecx->childp7ecx = childp7ecx;
   308 	SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
   310 	/* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
   311 	/* encoding process - we'll do that from the update function instead */
   312 	/* otherwise we'd be encoding data from a call of the notify function of the */
   313 	/* parent encoder (which would not work) */
   315     } else if (NSS_CMSType_IsData(childtype)) {
   316 	p7ecx->childp7ecx = NULL;
   317     } else {
   318 	/* we do not know this type */
   319 	p7ecx->error = SEC_ERROR_BAD_DER;
   320     }
   322     return SECSuccess;
   324 loser:
   325     if (childp7ecx) {
   326 	if (childp7ecx->ecx)
   327 	    SEC_ASN1EncoderFinish(childp7ecx->ecx);
   328 	PORT_Free(childp7ecx);
   329 	p7ecx->childp7ecx = NULL;
   330     }
   331     return SECFailure;
   332 }
   334 static SECStatus
   335 nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
   336 {
   337     SECStatus rv = SECFailure;
   339     switch (p7ecx->type) {
   340     case SEC_OID_PKCS7_SIGNED_DATA:
   341 	/* this will finish the digests and sign */
   342 	rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
   343 	break;
   344     case SEC_OID_PKCS7_ENVELOPED_DATA:
   345 	rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
   346 	break;
   347     case SEC_OID_PKCS7_DIGESTED_DATA:
   348 	rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
   349 	break;
   350     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   351 	rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
   352 	break;
   353     default:
   354         if (NSS_CMSType_IsWrapper(p7ecx->type)) {
   355 	    rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, p7ecx->content.genericData);
   356 	} else {
   357 	    rv = SECFailure;
   358 	}
   359 	break;
   360     }
   361     return rv;
   362 }
   364 /*
   365  * nss_cms_encoder_work_data - process incoming data
   366  *
   367  * (from the user or the next encoding layer)
   368  * Here, we need to digest and/or encrypt, then pass it on
   369  */
   370 static SECStatus
   371 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
   372 			     const unsigned char *data, unsigned long len,
   373 			     PRBool final, PRBool innermost)
   374 {
   375     unsigned char *buf = NULL;
   376     SECStatus rv;
   377     NSSCMSContentInfo *cinfo;
   379     rv = SECSuccess;		/* may as well be optimistic */
   381     /*
   382      * We should really have data to process, or we should be trying
   383      * to finish/flush the last block.  (This is an overly paranoid
   384      * check since all callers are in this file and simple inspection
   385      * proves they do it right.  But it could find a bug in future
   386      * modifications/development, that is why it is here.)
   387      */
   388     PORT_Assert ((data != NULL && len) || final);
   390     /* we got data (either from the caller, or from a lower level encoder) */
   391     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   392     if (!cinfo) {
   393 	/* The original programmer didn't expect this to happen */
   394 	p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   395 	return SECFailure;
   396     }
   398     /* Update the running digest. */
   399     if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL)
   400 	NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
   402     /* Encrypt this chunk. */
   403     if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
   404 	unsigned int inlen;	/* length of data being encrypted */
   405 	unsigned int outlen;	/* length of encrypted data */
   406 	unsigned int buflen;	/* length available for encrypted data */
   408 	inlen = len;
   409 	buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, inlen, final);
   410 	if (buflen == 0) {
   411 	    /*
   412 	     * No output is expected, but the input data may be buffered
   413 	     * so we still have to call Encrypt.
   414 	     */
   415 	    rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0,
   416 				   data, inlen, final);
   417 	    if (final) {
   418 		len = 0;
   419 		goto done;
   420 	    }
   421 	    return rv;
   422 	}
   424 	if (dest != NULL)
   425 	    buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
   426 	else
   427 	    buf = (unsigned char*)PORT_Alloc(buflen);
   429 	if (buf == NULL) {
   430 	    rv = SECFailure;
   431 	} else {
   432 	    rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
   433 				   data, inlen, final);
   434 	    data = buf;
   435 	    len = outlen;
   436 	}
   437 	if (rv != SECSuccess)
   438 	    /* encryption or malloc failed? */
   439 	    return rv;
   440     }
   443     /*
   444      * at this point (data,len) has everything we'd like to give to the CURRENT encoder
   445      * (which will encode it, then hand it back to the user or the parent encoder)
   446      * We don't encode the data if we're innermost and we're told not to include the data
   447      */
   448     if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != cinfo->content.pointer))
   449 	rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
   451 done:
   453     if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
   454 	if (dest != NULL) {
   455 	    dest->data = buf;
   456 	    dest->len = len;
   457 	} else if (buf != NULL) {
   458 	    PORT_Free (buf);
   459 	}
   460     }
   461     return rv;
   462 }
   464 /*
   465  * nss_cms_encoder_update - deliver encoded data to the next higher level
   466  *
   467  * no recursion here because we REALLY want to end up at the next higher encoder!
   468  */
   469 static SECStatus
   470 nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
   471 {
   472     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
   473     return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
   474 }
   476 /*
   477  * NSS_CMSEncoder_Start - set up encoding of a CMS message
   478  *
   479  * "cmsg" - message to encode
   480  * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
   481  *                           will not be called if NULL.
   482  * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
   483  * "destpoolp" - pool to allocate DER-encoded output in
   484  * "pwfn", pwfn_arg" - callback function for getting token password
   485  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
   486  * "detached_digestalgs", "detached_digests" - digests from detached content
   487  */
   488 NSSCMSEncoderContext *
   489 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
   490 			NSSCMSContentCallback outputfn, void *outputarg,
   491 			SECItem *dest, PLArenaPool *destpoolp,
   492 			PK11PasswordFunc pwfn, void *pwfn_arg,
   493 			NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
   494 			SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
   495 {
   496     NSSCMSEncoderContext *p7ecx;
   497     SECStatus rv;
   498     NSSCMSContentInfo *cinfo;
   499     SECOidTag tag;
   501     NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
   502 					detached_digestalgs, detached_digests);
   504     p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
   505     if (p7ecx == NULL) {
   506 	PORT_SetError(SEC_ERROR_NO_MEMORY);
   507 	return NULL;
   508     }
   510     p7ecx->cmsg = cmsg;
   511     p7ecx->output.outputfn = outputfn;
   512     p7ecx->output.outputarg = outputarg;
   513     p7ecx->output.dest = dest;
   514     p7ecx->output.destpoolp = destpoolp;
   515     p7ecx->type = SEC_OID_UNKNOWN;
   517     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
   519     tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   520     switch (tag) {
   521     case SEC_OID_PKCS7_SIGNED_DATA:
   522 	rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
   523 	break;
   524     case SEC_OID_PKCS7_ENVELOPED_DATA:
   525 	rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
   526 	break;
   527     case SEC_OID_PKCS7_DIGESTED_DATA:
   528 	rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
   529 	break;
   530     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   531 	rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
   532 	break;
   533     default:
   534         if (NSS_CMSType_IsWrapper(tag)) {
   535 	    rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, 
   536 						p7ecx->content.genericData);
   537 	} else {
   538 	    rv = SECFailure;
   539 	}
   540 	break;
   541     }
   542     if (rv != SECSuccess) {
   543 	PORT_Free(p7ecx);
   544 	return NULL;
   545     }
   547     /* Initialize the BER encoder.
   548      * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
   549     p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
   550 				       nss_cms_encoder_out, &(p7ecx->output));
   551     if (p7ecx->ecx == NULL) {
   552 	PORT_Free (p7ecx);
   553 	return NULL;
   554     }
   555     p7ecx->ecxupdated = PR_FALSE;
   557     /*
   558      * Indicate that we are streaming.  We will be streaming until we
   559      * get past the contents bytes.
   560      */
   561     if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
   562 	SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
   564     /*
   565      * The notify function will watch for the contents field.
   566      */
   567     SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
   569     /* this will kick off the encoding process & encode everything up to the content bytes,
   570      * at which point the notify function sets streaming mode (and possibly creates
   571      * a child encoder). */
   572     p7ecx->ecxupdated = PR_TRUE;
   573     if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
   574 	PORT_Free (p7ecx);
   575 	return NULL;
   576     }
   578     return p7ecx;
   579 }
   581 /*
   582  * NSS_CMSEncoder_Update - take content data delivery from the user
   583  *
   584  * "p7ecx" - encoder context
   585  * "data" - content data
   586  * "len" - length of content data
   587  *
   588  * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
   589  * then hand the data to the work_data fn
   590  */
   591 SECStatus
   592 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
   593 {
   594     SECStatus rv;
   595     NSSCMSContentInfo *cinfo;
   596     SECOidTag childtype;
   598     if (p7ecx->error)
   599 	return SECFailure;
   601     /* hand data to the innermost decoder */
   602     if (p7ecx->childp7ecx) {
   603 	/* tell the child to start encoding, up to its first data byte, if it
   604 	 * hasn't started yet */
   605 	if (!p7ecx->childp7ecx->ecxupdated) {
   606 	    p7ecx->childp7ecx->ecxupdated = PR_TRUE;
   607 	    if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess)
   608 	        return SECFailure;
   609 	}
   610 	/* recursion here */
   611 	rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
   612     } else {
   613 	/* we are at innermost decoder */
   614 	/* find out about our inner content type - must be data */
   615 	cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   616 	if (!cinfo) {
   617 	    /* The original programmer didn't expect this to happen */
   618 	    p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   619 	    return SECFailure;
   620 	}
   622 	childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
   623 	if (!NSS_CMSType_IsData(childtype))
   624 	    return SECFailure;
   625 	/* and we must not have preset data */
   626 	if (cinfo->content.data != NULL)
   627 	    return SECFailure;
   629 	/*  hand it the data so it can encode it (let DER trickle up the chain) */
   630 	rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
   631     }
   632     return rv;
   633 }
   635 /*
   636  * NSS_CMSEncoder_Cancel - stop all encoding
   637  *
   638  * we need to walk down the chain of encoders and the finish them from the innermost out
   639  */
   640 SECStatus
   641 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
   642 {
   643     SECStatus rv = SECFailure;
   645     /* XXX do this right! */
   647     /*
   648      * Finish any inner decoders before us so that all the encoded data is flushed
   649      * This basically finishes all the decoders from the innermost to the outermost.
   650      * Finishing an inner decoder may result in data being updated to the outer decoder
   651      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
   652      */
   653     if (p7ecx->childp7ecx) {
   654 	rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   655 	/* remember rv for now */
   656     }
   658     /*
   659      * On the way back up, there will be no more data (if we had an
   660      * inner encoder, it is done now!)
   661      * Flush out any remaining data and/or finish digests.
   662      */
   663     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
   664     if (rv != SECSuccess)
   665 	goto loser;
   667     p7ecx->childp7ecx = NULL;
   669     /* kick the encoder back into working mode again.
   670      * We turn off streaming stuff (which will cause the encoder to continue
   671      * encoding happily, now that we have all the data (like digests) ready for it).
   672      */
   673     SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
   674     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
   676     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
   677     rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
   679 loser:
   680     SEC_ASN1EncoderFinish(p7ecx->ecx);
   681     PORT_Free (p7ecx);
   682     return rv;
   683 }
   685 /*
   686  * NSS_CMSEncoder_Finish - signal the end of data
   687  *
   688  * we need to walk down the chain of encoders and the finish them from the innermost out
   689  */
   690 SECStatus
   691 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
   692 {
   693     SECStatus rv = SECFailure;
   694     NSSCMSContentInfo *cinfo;
   696     /*
   697      * Finish any inner decoders before us so that all the encoded data is flushed
   698      * This basically finishes all the decoders from the innermost to the outermost.
   699      * Finishing an inner decoder may result in data being updated to the outer decoder
   700      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
   701      */
   702     if (p7ecx->childp7ecx) {
   703 	/* tell the child to start encoding, up to its first data byte, if it
   704 	 * hasn't yet */
   705 	if (!p7ecx->childp7ecx->ecxupdated) {
   706 	    p7ecx->childp7ecx->ecxupdated = PR_TRUE;
   707 	    rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0);
   708 	    if (rv != SECSuccess) {
   709 		NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   710 		goto loser;
   711 	    }
   712 	}
   713 	rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
   714 	if (rv != SECSuccess)
   715 	    goto loser;
   716     }
   718     /*
   719      * On the way back up, there will be no more data (if we had an
   720      * inner encoder, it is done now!)
   721      * Flush out any remaining data and/or finish digests.
   722      */
   723     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
   724     if (rv != SECSuccess)
   725 	goto loser;
   727     p7ecx->childp7ecx = NULL;
   729     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
   730     if (!cinfo) {
   731 	/* The original programmer didn't expect this to happen */
   732 	p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
   733 	rv = SECFailure;
   734 	goto loser;
   735     }
   736     SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
   737     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
   738     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
   739     rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
   741     if (p7ecx->error)
   742 	rv = SECFailure;
   744 loser:
   745     SEC_ASN1EncoderFinish(p7ecx->ecx);
   746     PORT_Free (p7ecx);
   747     return rv;
   748 }

mercurial