Back to index

lightning-sunbird  0.9+nobinonly
cmsencode.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * CMS encoding.
00039  *
00040  * $Id: cmsencode.c,v 1.5.28.1 2006/06/08 21:59:27 nelson%bolyard.com Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "cert.h"
00046 #include "key.h"
00047 #include "secasn1.h"
00048 #include "secoid.h"
00049 #include "secitem.h"
00050 #include "pk11func.h"
00051 #include "secerr.h"
00052 
00053 struct nss_cms_encoder_output {
00054     NSSCMSContentCallback outputfn;
00055     void *outputarg;
00056     PLArenaPool *destpoolp;
00057     SECItem *dest;
00058 };
00059 
00060 struct NSSCMSEncoderContextStr {
00061     SEC_ASN1EncoderContext *       ecx;          /* ASN.1 encoder context */
00062     PRBool                  ecxupdated;   /* true if data was handed in */
00063     NSSCMSMessage *         cmsg;         /* pointer to the root message */
00064     SECOidTag               type;         /* type tag of the current content */
00065     NSSCMSContent           content;      /* pointer to current content */
00066     struct nss_cms_encoder_output output; /* output function */
00067     int                            error;        /* error code */
00068     NSSCMSEncoderContext *  childp7ecx;   /* link to child encoder context */
00069 };
00070 
00071 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
00072 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
00073 static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
00074 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
00075                           const unsigned char *data, unsigned long len,
00076                           PRBool final, PRBool innermost);
00077 
00078 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
00079 
00080 /*
00081  * The little output function that the ASN.1 encoder calls to hand
00082  * us bytes which we in turn hand back to our caller (via the callback
00083  * they gave us).
00084  */
00085 static void
00086 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
00087                     int depth, SEC_ASN1EncodingPart data_kind)
00088 {
00089     struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
00090     unsigned char *dest;
00091     unsigned long offset;
00092 
00093 #ifdef CMSDEBUG
00094     int i;
00095 
00096     fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len);
00097     for (i=0; i < len; i++) {
00098        fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
00099     }
00100     if ((i % 16) != 0)
00101        fprintf(stderr, "\n");
00102 #endif
00103 
00104     if (output->outputfn != NULL)
00105        /* call output callback with DER data */
00106        output->outputfn(output->outputarg, buf, len);
00107 
00108     if (output->dest != NULL) {
00109        /* store DER data in SECItem */
00110        offset = output->dest->len;
00111        if (offset == 0) {
00112            dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
00113        } else {
00114            dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 
00115                               output->dest->data,
00116                               output->dest->len,
00117                               output->dest->len + len);
00118        }
00119        if (dest == NULL)
00120            /* oops */
00121            return;
00122 
00123        output->dest->data = dest;
00124        output->dest->len += len;
00125 
00126        /* copy it in */
00127        PORT_Memcpy(output->dest->data + offset, buf, len);
00128     }
00129 }
00130 
00131 /*
00132  * nss_cms_encoder_notify - ASN.1 encoder callback
00133  *
00134  * this function is called by the ASN.1 encoder before and after the encoding of
00135  * every object. here, it is used to keep track of data structures, set up
00136  * encryption and/or digesting and possibly set up child encoders.
00137  */
00138 static void
00139 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
00140 {
00141     NSSCMSEncoderContext *p7ecx;
00142     NSSCMSContentInfo *rootcinfo, *cinfo;
00143     PRBool after = !before;
00144     PLArenaPool *poolp;
00145     SECOidTag childtype;
00146     SECItem *item;
00147 
00148     p7ecx = (NSSCMSEncoderContext *)arg;
00149     PORT_Assert(p7ecx != NULL);
00150 
00151     rootcinfo = &(p7ecx->cmsg->contentInfo);
00152     poolp = p7ecx->cmsg->poolp;
00153 
00154 #ifdef CMSDEBUG
00155     fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
00156 #endif
00157 
00158     /*
00159      * Watch for the content field, at which point we want to instruct
00160      * the ASN.1 encoder to start taking bytes from the buffer.
00161      */
00162     switch (p7ecx->type) {
00163     default:
00164     case SEC_OID_UNKNOWN:
00165        /* we're still in the root message */
00166        if (after && dest == &(rootcinfo->contentType)) {
00167            /* got the content type OID now - so find out the type tag */
00168            p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
00169            /* set up a pointer to our current content */
00170            p7ecx->content = rootcinfo->content;
00171        }
00172        break;
00173 
00174     case SEC_OID_PKCS7_DATA:
00175        if (before && dest == &(rootcinfo->rawContent)) {
00176            /* just set up encoder to grab from user - no encryption or digesting */
00177            if ((item = rootcinfo->content.data) != NULL)
00178               (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
00179            else
00180               SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
00181            SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);  /* no need to get notified anymore */
00182        }
00183        break;
00184 
00185     case SEC_OID_PKCS7_SIGNED_DATA:
00186     case SEC_OID_PKCS7_ENVELOPED_DATA:
00187     case SEC_OID_PKCS7_DIGESTED_DATA:
00188     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00189 
00190        /* when we know what the content is, we encode happily until we reach the inner content */
00191        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
00192        childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00193 
00194        if (after && dest == &(cinfo->contentType)) {
00195            /* we're right before encoding the data (if we have some or not) */
00196            /* (for encrypted data, we're right before the contentEncAlg which may change */
00197            /*  in nss_cms_before_data because of IV calculation when setting up encryption) */
00198            if (nss_cms_before_data(p7ecx) != SECSuccess)
00199               p7ecx->error = PORT_GetError();
00200        }
00201        if (before && dest == &(cinfo->rawContent)) {
00202            if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
00203               /* we have data - feed it in */
00204               (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
00205            else
00206               /* else try to get it from user */
00207               SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
00208        }
00209        if (after && dest == &(cinfo->rawContent)) {
00210            if (nss_cms_after_data(p7ecx) != SECSuccess)
00211               p7ecx->error = PORT_GetError();
00212            SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx);  /* no need to get notified anymore */
00213        }
00214        break;
00215     }
00216 }
00217 
00218 /*
00219  * nss_cms_before_data - setup the current encoder to receive data
00220  */
00221 static SECStatus
00222 nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
00223 {
00224     SECStatus rv;
00225     SECOidTag childtype;
00226     NSSCMSContentInfo *cinfo;
00227     PLArenaPool *poolp;
00228     NSSCMSEncoderContext *childp7ecx;
00229     const SEC_ASN1Template *template;
00230 
00231     poolp = p7ecx->cmsg->poolp;
00232 
00233     /* call _Encode_BeforeData handlers */
00234     switch (p7ecx->type) {
00235     case SEC_OID_PKCS7_SIGNED_DATA:
00236        /* we're encoding a signedData, so set up the digests */
00237        rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
00238        break;
00239     case SEC_OID_PKCS7_DIGESTED_DATA:
00240        /* we're encoding a digestedData, so set up the digest */
00241        rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
00242        break;
00243     case SEC_OID_PKCS7_ENVELOPED_DATA:
00244        rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
00245        break;
00246     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00247        rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
00248        break;
00249     default:
00250        rv = SECFailure;
00251     }
00252     if (rv != SECSuccess)
00253        return SECFailure;
00254 
00255     /* ok, now we have a pointer to cinfo */
00256     /* find out what kind of data is encapsulated */
00257     
00258     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
00259     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00260 
00261     switch (childtype) {
00262     case SEC_OID_PKCS7_SIGNED_DATA:
00263     case SEC_OID_PKCS7_ENVELOPED_DATA:
00264     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00265     case SEC_OID_PKCS7_DIGESTED_DATA:
00266 #if 0
00267     case SEC_OID_PKCS7_DATA:              /* XXX here also??? maybe yes! */
00268 #endif
00269        /* in these cases, we need to set up a child encoder! */
00270        /* create new encoder context */
00271        childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
00272        if (childp7ecx == NULL)
00273            return SECFailure;
00274 
00275        /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
00276         * (which will encrypt and/or digest it)
00277         * this needs to route back into our update function
00278         * which finds the lowest encoding context & encrypts and computes digests */
00279        childp7ecx->type = childtype;
00280        childp7ecx->content = cinfo->content;
00281        /* use the non-recursive update function here, of course */
00282        childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
00283        childp7ecx->output.outputarg = p7ecx;
00284        childp7ecx->output.destpoolp = NULL;
00285        childp7ecx->output.dest = NULL;
00286        childp7ecx->cmsg = p7ecx->cmsg;
00287 
00288        template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
00289        if (template == NULL)
00290            goto loser;             /* cannot happen */
00291 
00292        /* now initialize the data for encoding the first third */
00293        switch (childp7ecx->type) {
00294        case SEC_OID_PKCS7_SIGNED_DATA:
00295            rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
00296            break;
00297        case SEC_OID_PKCS7_ENVELOPED_DATA:
00298            rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
00299            break;
00300        case SEC_OID_PKCS7_DIGESTED_DATA:
00301            rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
00302            break;
00303        case SEC_OID_PKCS7_ENCRYPTED_DATA:
00304            rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
00305            break;
00306        case SEC_OID_PKCS7_DATA:
00307            rv = SECSuccess;
00308            break;
00309        default:
00310            PORT_Assert(0);
00311            break;
00312        }
00313        if (rv != SECSuccess)
00314            goto loser;
00315 
00316        /*
00317         * Initialize the BER encoder.
00318         */
00319        childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
00320                                       nss_cms_encoder_out, &(childp7ecx->output));
00321        if (childp7ecx->ecx == NULL)
00322            goto loser;
00323 
00324        childp7ecx->ecxupdated = PR_FALSE;
00325 
00326        /*
00327         * Indicate that we are streaming.  We will be streaming until we
00328         * get past the contents bytes.
00329         */
00330        SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
00331 
00332        /*
00333         * The notify function will watch for the contents field.
00334         */
00335        SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
00336 
00337        /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
00338        /* encoding process - we'll do that from the update function instead */
00339        /* otherwise we'd be encoding data from a call of the notify function of the */
00340        /* parent encoder (which would not work) */
00341 
00342        /* this will kick off the encoding process & encode everything up to the content bytes,
00343         * at which point the notify function sets streaming mode (and possibly creates
00344         * another child encoder). */
00345        if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
00346            goto loser;
00347 
00348        p7ecx->childp7ecx = childp7ecx;
00349        break;
00350 
00351     case SEC_OID_PKCS7_DATA:
00352        p7ecx->childp7ecx = NULL;
00353        break;
00354     default:
00355        /* we do not know this type */
00356        p7ecx->error = SEC_ERROR_BAD_DER;
00357        break;
00358     }
00359 
00360     return SECSuccess;
00361 
00362 loser:
00363     if (childp7ecx) {
00364        if (childp7ecx->ecx)
00365            SEC_ASN1EncoderFinish(childp7ecx->ecx);
00366        PORT_Free(childp7ecx);
00367     }
00368     return SECFailure;
00369 }
00370 
00371 static SECStatus
00372 nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
00373 {
00374     SECStatus rv = SECFailure;
00375 
00376     switch (p7ecx->type) {
00377     case SEC_OID_PKCS7_SIGNED_DATA:
00378        /* this will finish the digests and sign */
00379        rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
00380        break;
00381     case SEC_OID_PKCS7_ENVELOPED_DATA:
00382        rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
00383        break;
00384     case SEC_OID_PKCS7_DIGESTED_DATA:
00385        rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
00386        break;
00387     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00388        rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
00389        break;
00390     case SEC_OID_PKCS7_DATA:
00391        /* do nothing */
00392        break;
00393     default:
00394        rv = SECFailure;
00395        break;
00396     }
00397     return rv;
00398 }
00399 
00400 /*
00401  * nss_cms_encoder_work_data - process incoming data
00402  *
00403  * (from the user or the next encoding layer)
00404  * Here, we need to digest and/or encrypt, then pass it on
00405  */
00406 static SECStatus
00407 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
00408                           const unsigned char *data, unsigned long len,
00409                           PRBool final, PRBool innermost)
00410 {
00411     unsigned char *buf = NULL;
00412     SECStatus rv;
00413     NSSCMSContentInfo *cinfo;
00414 
00415     rv = SECSuccess;        /* may as well be optimistic */
00416 
00417     /*
00418      * We should really have data to process, or we should be trying
00419      * to finish/flush the last block.  (This is an overly paranoid
00420      * check since all callers are in this file and simple inspection
00421      * proves they do it right.  But it could find a bug in future
00422      * modifications/development, that is why it is here.)
00423      */
00424     PORT_Assert ((data != NULL && len) || final);
00425 
00426     /* we got data (either from the caller, or from a lower level encoder) */
00427     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
00428 
00429     /* Update the running digest. */
00430     if (len && cinfo->digcx != NULL)
00431        NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
00432 
00433     /* Encrypt this chunk. */
00434     if (cinfo->ciphcx != NULL) {
00435        unsigned int inlen;  /* length of data being encrypted */
00436        unsigned int outlen; /* length of encrypted data */
00437        unsigned int buflen; /* length available for encrypted data */
00438 
00439        inlen = len;
00440        buflen = NSS_CMSCipherContext_EncryptLength(cinfo->ciphcx, inlen, final);
00441        if (buflen == 0) {
00442            /*
00443             * No output is expected, but the input data may be buffered
00444             * so we still have to call Encrypt.
00445             */
00446            rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, NULL, NULL, 0,
00447                                data, inlen, final);
00448            if (final) {
00449               len = 0;
00450               goto done;
00451            }
00452            return rv;
00453        }
00454 
00455        if (dest != NULL)
00456            buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
00457        else
00458            buf = (unsigned char*)PORT_Alloc(buflen);
00459 
00460        if (buf == NULL) {
00461            rv = SECFailure;
00462        } else {
00463            rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, buf, &outlen, buflen,
00464                                data, inlen, final);
00465            data = buf;
00466            len = outlen;
00467        }
00468        if (rv != SECSuccess)
00469            /* encryption or malloc failed? */
00470            return rv;
00471     }
00472 
00473 
00474     /*
00475      * at this point (data,len) has everything we'd like to give to the CURRENT encoder
00476      * (which will encode it, then hand it back to the user or the parent encoder)
00477      * We don't encode the data if we're innermost and we're told not to include the data
00478      */
00479     if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
00480        rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
00481 
00482 done:
00483 
00484     if (cinfo->ciphcx != NULL) {
00485        if (dest != NULL) {
00486            dest->data = buf;
00487            dest->len = len;
00488        } else if (buf != NULL) {
00489            PORT_Free (buf);
00490        }
00491     }
00492     return rv;
00493 }
00494 
00495 /*
00496  * nss_cms_encoder_update - deliver encoded data to the next higher level
00497  *
00498  * no recursion here because we REALLY want to end up at the next higher encoder!
00499  */
00500 static SECStatus
00501 nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
00502 {
00503     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
00504     return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
00505 }
00506 
00507 /*
00508  * NSS_CMSEncoder_Start - set up encoding of a CMS message
00509  *
00510  * "cmsg" - message to encode
00511  * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
00512  *                           will not be called if NULL.
00513  * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
00514  * "destpoolp" - pool to allocate DER-encoded output in
00515  * "pwfn", pwfn_arg" - callback function for getting token password
00516  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
00517  * "detached_digestalgs", "detached_digests" - digests from detached content
00518  */
00519 NSSCMSEncoderContext *
00520 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
00521                      NSSCMSContentCallback outputfn, void *outputarg,
00522                      SECItem *dest, PLArenaPool *destpoolp,
00523                      PK11PasswordFunc pwfn, void *pwfn_arg,
00524                      NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
00525                      SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
00526 {
00527     NSSCMSEncoderContext *p7ecx;
00528     SECStatus rv;
00529     NSSCMSContentInfo *cinfo;
00530 
00531     NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
00532                                    detached_digestalgs, detached_digests);
00533 
00534     p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
00535     if (p7ecx == NULL) {
00536        PORT_SetError(SEC_ERROR_NO_MEMORY);
00537        return NULL;
00538     }
00539 
00540     p7ecx->cmsg = cmsg;
00541     p7ecx->output.outputfn = outputfn;
00542     p7ecx->output.outputarg = outputarg;
00543     p7ecx->output.dest = dest;
00544     p7ecx->output.destpoolp = destpoolp;
00545     p7ecx->type = SEC_OID_UNKNOWN;
00546 
00547     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
00548 
00549     switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
00550     case SEC_OID_PKCS7_SIGNED_DATA:
00551        rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
00552        break;
00553     case SEC_OID_PKCS7_ENVELOPED_DATA:
00554        rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
00555        break;
00556     case SEC_OID_PKCS7_DIGESTED_DATA:
00557        rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
00558        break;
00559     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00560        rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
00561        break;
00562     default:
00563        rv = SECFailure;
00564        break;
00565     }
00566     if (rv != SECSuccess) {
00567        PORT_Free(p7ecx);
00568        return NULL;
00569     }
00570 
00571     /* Initialize the BER encoder.
00572      * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
00573     p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
00574                                    nss_cms_encoder_out, &(p7ecx->output));
00575     if (p7ecx->ecx == NULL) {
00576        PORT_Free (p7ecx);
00577        return NULL;
00578     }
00579     p7ecx->ecxupdated = PR_FALSE;
00580 
00581     /*
00582      * Indicate that we are streaming.  We will be streaming until we
00583      * get past the contents bytes.
00584      */
00585     SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
00586 
00587     /*
00588      * The notify function will watch for the contents field.
00589      */
00590     SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
00591 
00592     /* this will kick off the encoding process & encode everything up to the content bytes,
00593      * at which point the notify function sets streaming mode (and possibly creates
00594      * a child encoder). */
00595     if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
00596        PORT_Free (p7ecx);
00597        return NULL;
00598     }
00599 
00600     return p7ecx;
00601 }
00602 
00603 /*
00604  * NSS_CMSEncoder_Update - take content data delivery from the user
00605  *
00606  * "p7ecx" - encoder context
00607  * "data" - content data
00608  * "len" - length of content data
00609  *
00610  * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
00611  * then hand the data to the work_data fn
00612  */
00613 SECStatus
00614 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
00615 {
00616     SECStatus rv;
00617     NSSCMSContentInfo *cinfo;
00618     SECOidTag childtype;
00619 
00620     if (p7ecx->error)
00621        return SECFailure;
00622 
00623     /* hand data to the innermost decoder */
00624     if (p7ecx->childp7ecx) {
00625        /* recursion here */
00626        rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
00627     } else {
00628        /* we are at innermost decoder */
00629        /* find out about our inner content type - must be data */
00630        cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
00631        childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00632        if (childtype != SEC_OID_PKCS7_DATA)
00633            return SECFailure;
00634        /* and we must not have preset data */
00635        if (cinfo->content.data != NULL)
00636            return SECFailure;
00637 
00638        /*  hand it the data so it can encode it (let DER trickle up the chain) */
00639        rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
00640     }
00641     return rv;
00642 }
00643 
00644 /*
00645  * NSS_CMSEncoder_Cancel - stop all encoding
00646  *
00647  * we need to walk down the chain of encoders and the finish them from the innermost out
00648  */
00649 SECStatus
00650 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
00651 {
00652     SECStatus rv = SECFailure;
00653 
00654     /* XXX do this right! */
00655 
00656     /*
00657      * Finish any inner decoders before us so that all the encoded data is flushed
00658      * This basically finishes all the decoders from the innermost to the outermost.
00659      * Finishing an inner decoder may result in data being updated to the outer decoder
00660      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
00661      */
00662     if (p7ecx->childp7ecx) {
00663        rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
00664        /* remember rv for now */
00665     }
00666 
00667     /*
00668      * On the way back up, there will be no more data (if we had an
00669      * inner encoder, it is done now!)
00670      * Flush out any remaining data and/or finish digests.
00671      */
00672     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
00673     if (rv != SECSuccess)
00674        goto loser;
00675 
00676     p7ecx->childp7ecx = NULL;
00677 
00678     /* kick the encoder back into working mode again.
00679      * We turn off streaming stuff (which will cause the encoder to continue
00680      * encoding happily, now that we have all the data (like digests) ready for it).
00681      */
00682     SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
00683     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
00684 
00685     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
00686     rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
00687 
00688 loser:
00689     SEC_ASN1EncoderFinish(p7ecx->ecx);
00690     PORT_Free (p7ecx);
00691     return rv;
00692 }
00693 
00694 /*
00695  * NSS_CMSEncoder_Finish - signal the end of data
00696  *
00697  * we need to walk down the chain of encoders and the finish them from the innermost out
00698  */
00699 SECStatus
00700 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
00701 {
00702     SECStatus rv = SECFailure;
00703     NSSCMSContentInfo *cinfo;
00704     SECOidTag childtype;
00705 
00706     /*
00707      * Finish any inner decoders before us so that all the encoded data is flushed
00708      * This basically finishes all the decoders from the innermost to the outermost.
00709      * Finishing an inner decoder may result in data being updated to the outer decoder
00710      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
00711      */
00712     if (p7ecx->childp7ecx) {
00713        rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
00714        if (rv != SECSuccess)
00715            goto loser;
00716     }
00717 
00718     /*
00719      * On the way back up, there will be no more data (if we had an
00720      * inner encoder, it is done now!)
00721      * Flush out any remaining data and/or finish digests.
00722      */
00723     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
00724     if (rv != SECSuccess)
00725        goto loser;
00726 
00727     p7ecx->childp7ecx = NULL;
00728 
00729     /* find out about our inner content type - must be data */
00730     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
00731     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00732     if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
00733        SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
00734        /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
00735        rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
00736     }
00737 
00738     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
00739 
00740     if (p7ecx->error)
00741        rv = SECFailure;
00742 
00743 loser:
00744     SEC_ASN1EncoderFinish(p7ecx->ecx);
00745     PORT_Free (p7ecx);
00746     return rv;
00747 }