Back to index

lightning-sunbird  0.9+nobinonly
cmsdecode.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 decoding.
00039  *
00040  * $Id: cmsdecode.c,v 1.8 2004/04/25 15:03:16 gerv%gerv.net Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "cert.h"
00046 #include "key.h"
00047 #include "secasn1.h"
00048 #include "secitem.h"
00049 #include "secoid.h"
00050 #include "prtime.h"
00051 #include "secerr.h"
00052 
00053 struct NSSCMSDecoderContextStr {
00054     SEC_ASN1DecoderContext *       dcx;          /* ASN.1 decoder context */
00055     NSSCMSMessage *         cmsg;         /* backpointer to the root message */
00056     SECOidTag               type;         /* type of message */
00057     NSSCMSContent           content;      /* pointer to message */
00058     NSSCMSDecoderContext *  childp7dcx;   /* inner CMS decoder context */
00059     PRBool                  saw_contents;
00060     int                            error;
00061     NSSCMSContentCallback   cb;
00062     void *                  cb_arg;
00063 };
00064 
00065 struct NSSCMSDecoderDataStr {
00066     SECItem data;    /* must be first */
00067     unsigned int totalBufferSize;
00068 };
00069 
00070 typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData;
00071 
00072 static void      nss_cms_decoder_update_filter (void *arg, const char *data, 
00073                  unsigned long len, int depth, SEC_ASN1EncodingPart data_kind);
00074 static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx);
00075 static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx);
00076 static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx);
00077 static void      nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 
00078                const unsigned char *data, unsigned long len, PRBool final);
00079 static NSSCMSDecoderData *nss_cms_create_decoder_data(PRArenaPool *poolp);
00080 
00081 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
00082 
00083 static NSSCMSDecoderData *
00084 nss_cms_create_decoder_data(PRArenaPool *poolp)
00085 {
00086     NSSCMSDecoderData *decoderData = NULL;
00087 
00088     decoderData = (NSSCMSDecoderData *)
00089                      PORT_ArenaAlloc(poolp,sizeof(NSSCMSDecoderData));
00090     if (!decoderData) {
00091        return NULL;
00092     }
00093     decoderData->data.data = NULL;
00094     decoderData->data.len = 0;
00095     decoderData->totalBufferSize = 0;
00096     return decoderData;
00097 }
00098 
00099 /* 
00100  * nss_cms_decoder_notify -
00101  *  this is the driver of the decoding process. It gets called by the ASN.1
00102  *  decoder before and after an object is decoded.
00103  *  at various points in the decoding process, we intercept to set up and do
00104  *  further processing.
00105  */
00106 static void
00107 nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
00108 {
00109     NSSCMSDecoderContext *p7dcx;
00110     NSSCMSContentInfo *rootcinfo, *cinfo;
00111     PRBool after = !before;
00112 
00113     p7dcx = (NSSCMSDecoderContext *)arg;
00114     rootcinfo = &(p7dcx->cmsg->contentInfo);
00115 
00116     /* XXX error handling: need to set p7dcx->error */
00117 
00118 #ifdef CMSDEBUG 
00119     fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
00120 #endif
00121 
00122     /* so what are we working on right now? */
00123     switch (p7dcx->type) {
00124     case SEC_OID_UNKNOWN:
00125        /*
00126         * right now, we are still decoding the OUTER (root) cinfo
00127         * As soon as we know the inner content type, set up the info,
00128         * but NO inner decoder or filter. The root decoder handles the first
00129         * level children by itself - only for encapsulated contents (which
00130         * are encoded as DER inside of an OCTET STRING) we need to set up a
00131         * child decoder...
00132         */
00133        if (after && dest == &(rootcinfo->contentType)) {
00134            p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
00135            p7dcx->content = rootcinfo->content;  
00136            /* is this ready already ? need to alloc? */
00137            /* XXX yes we need to alloc -- continue here */
00138        }
00139        break;
00140     case SEC_OID_PKCS7_DATA:
00141        /* this can only happen if the outermost cinfo has DATA in it */
00142        /* otherwise, we handle this type implicitely in the inner decoders */
00143 
00144        if (before && dest == &(rootcinfo->content)) {
00145            /* cause the filter to put the data in the right place... 
00146            ** We want the ASN.1 decoder to deliver the decoded bytes to us 
00147            ** from now on 
00148            */
00149            SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
00150                                      nss_cms_decoder_update_filter,
00151                                      p7dcx,
00152                                      (PRBool)(p7dcx->cb != NULL));
00153            break;
00154        }
00155 
00156        if (after && dest == &(rootcinfo->content.data)) {
00157            /* remove the filter */
00158            SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
00159        }
00160        break;
00161 
00162     case SEC_OID_PKCS7_SIGNED_DATA:
00163     case SEC_OID_PKCS7_ENVELOPED_DATA:
00164     case SEC_OID_PKCS7_DIGESTED_DATA:
00165     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00166 
00167        if (before && dest == &(rootcinfo->content))
00168            break;     /* we're not there yet */
00169 
00170        if (p7dcx->content.pointer == NULL)
00171            p7dcx->content = rootcinfo->content;
00172 
00173        /* get this data type's inner contentInfo */
00174        cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, 
00175                                              p7dcx->type);
00176 
00177        if (before && dest == &(cinfo->contentType)) {
00178            /* at this point, set up the &%$&$ back pointer */
00179            /* we cannot do it later, because the content itself is optional! */
00180            /* please give me C++ */
00181            switch (p7dcx->type) {
00182            case SEC_OID_PKCS7_SIGNED_DATA:
00183               p7dcx->content.signedData->cmsg = p7dcx->cmsg;
00184               break;
00185            case SEC_OID_PKCS7_DIGESTED_DATA:
00186               p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
00187               break;
00188            case SEC_OID_PKCS7_ENVELOPED_DATA:
00189               p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
00190               break;
00191            case SEC_OID_PKCS7_ENCRYPTED_DATA:
00192               p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
00193               break;
00194            default:
00195               PORT_Assert(0);
00196               break;
00197            }
00198        }
00199 
00200        if (before && dest == &(cinfo->rawContent)) {
00201            /* we want the ASN.1 decoder to deliver the decoded bytes to us 
00202            ** from now on 
00203            */
00204            SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, 
00205                                         nss_cms_decoder_update_filter, 
00206                                     p7dcx, (PRBool)(p7dcx->cb != NULL));
00207 
00208 
00209            /* we're right in front of the data */
00210            if (nss_cms_before_data(p7dcx) != SECSuccess) {
00211               SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);      
00212               /* stop all processing */
00213               p7dcx->error = PORT_GetError();
00214            }
00215        }
00216        if (after && dest == &(cinfo->rawContent)) {
00217            /* we're right after of the data */
00218            if (nss_cms_after_data(p7dcx) != SECSuccess)
00219               p7dcx->error = PORT_GetError();
00220 
00221            /* we don't need to see the contents anymore */
00222            SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
00223        }
00224        break;
00225 
00226 #if 0 /* NIH */
00227     case SEC_OID_PKCS7_AUTHENTICATED_DATA:
00228 #endif
00229     default:
00230        /* unsupported or unknown message type - fail  gracefully */
00231        p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
00232        break;
00233     }
00234 }
00235 
00236 /*
00237  * nss_cms_before_data - set up the current encoder to receive data
00238  */
00239 static SECStatus
00240 nss_cms_before_data(NSSCMSDecoderContext *p7dcx)
00241 {
00242     SECStatus rv;
00243     SECOidTag childtype;
00244     PLArenaPool *poolp;
00245     NSSCMSDecoderContext *childp7dcx;
00246     NSSCMSContentInfo *cinfo;
00247     const SEC_ASN1Template *template;
00248     void *mark = NULL;
00249     size_t size;
00250     
00251     poolp = p7dcx->cmsg->poolp;
00252 
00253     /* call _Decode_BeforeData handlers */
00254     switch (p7dcx->type) {
00255     case SEC_OID_PKCS7_SIGNED_DATA:
00256        /* we're decoding a signedData, so set up the digests */
00257        rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData);
00258        break;
00259     case SEC_OID_PKCS7_DIGESTED_DATA:
00260        /* we're encoding a digestedData, so set up the digest */
00261        rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData);
00262        break;
00263     case SEC_OID_PKCS7_ENVELOPED_DATA:
00264        rv = NSS_CMSEnvelopedData_Decode_BeforeData(
00265                                     p7dcx->content.envelopedData);
00266        break;
00267     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00268        rv = NSS_CMSEncryptedData_Decode_BeforeData(
00269                                     p7dcx->content.encryptedData);
00270        break;
00271     default:
00272        return SECFailure;
00273     }
00274     if (rv != SECSuccess)
00275        return SECFailure;
00276 
00277     /* ok, now we have a pointer to cinfo */
00278     /* find out what kind of data is encapsulated */
00279     
00280     cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
00281     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00282 
00283     if (childtype == SEC_OID_PKCS7_DATA) {
00284        cinfo->content.pointer = (void *) nss_cms_create_decoder_data(poolp);
00285        if (cinfo->content.pointer == NULL)
00286            /* set memory error */
00287            return SECFailure;
00288 
00289        p7dcx->childp7dcx = NULL;
00290        return SECSuccess;
00291     }
00292 
00293     /* set up inner decoder */
00294 
00295     if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL)
00296        return SECFailure;
00297 
00298     childp7dcx = PORT_ZNew(NSSCMSDecoderContext);
00299     if (childp7dcx == NULL)
00300        return SECFailure;
00301 
00302     mark = PORT_ArenaMark(poolp);
00303 
00304     /* allocate space for the stuff we're creating */
00305     size = NSS_CMSUtil_GetSizeByTypeTag(childtype);
00306     childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
00307     if (childp7dcx->content.pointer == NULL)
00308        goto loser;
00309 
00310     /* start the child decoder */
00311     childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, 
00312                                            template);
00313     if (childp7dcx->dcx == NULL)
00314        goto loser;
00315 
00316     /* the new decoder needs to notify, too */
00317     SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, 
00318                                  childp7dcx);
00319 
00320     /* tell the parent decoder that it needs to feed us the content data */
00321     p7dcx->childp7dcx = childp7dcx;
00322 
00323     childp7dcx->type = childtype;  /* our type */
00324 
00325     childp7dcx->cmsg = p7dcx->cmsg;       /* backpointer to root message */
00326 
00327     /* should the child decoder encounter real data, 
00328     ** it must give it to the caller 
00329     */
00330     childp7dcx->cb = p7dcx->cb;
00331     childp7dcx->cb_arg = p7dcx->cb_arg;
00332 
00333     /* now set up the parent to hand decoded data to the next level decoder */
00334     p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update;
00335     p7dcx->cb_arg = childp7dcx;
00336 
00337     PORT_ArenaUnmark(poolp, mark);
00338 
00339     return SECSuccess;
00340 
00341 loser:
00342     if (mark)
00343        PORT_ArenaRelease(poolp, mark);
00344     if (childp7dcx)
00345        PORT_Free(childp7dcx);
00346     p7dcx->childp7dcx = NULL;
00347     return SECFailure;
00348 }
00349 
00350 static SECStatus
00351 nss_cms_after_data(NSSCMSDecoderContext *p7dcx)
00352 {
00353     NSSCMSDecoderContext *childp7dcx;
00354     SECStatus rv = SECFailure;
00355 
00356     /* Handle last block. This is necessary to flush out the last bytes
00357      * of a possibly incomplete block */
00358     nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
00359 
00360     /* finish any "inner" decoders - there's no more data coming... */
00361     if (p7dcx->childp7dcx != NULL) {
00362        childp7dcx = p7dcx->childp7dcx;
00363        if (childp7dcx->dcx != NULL) {
00364            if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
00365               /* do what? free content? */
00366               rv = SECFailure;
00367            } else {
00368               rv = nss_cms_after_end(childp7dcx);
00369            }
00370            if (rv != SECSuccess)
00371               goto done;
00372        }
00373        PORT_Free(p7dcx->childp7dcx);
00374        p7dcx->childp7dcx = NULL;
00375     }
00376 
00377     switch (p7dcx->type) {
00378     case SEC_OID_PKCS7_SIGNED_DATA:
00379        /* this will finish the digests and verify */
00380        rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData);
00381        break;
00382     case SEC_OID_PKCS7_ENVELOPED_DATA:
00383        rv = NSS_CMSEnvelopedData_Decode_AfterData(
00384                                    p7dcx->content.envelopedData);
00385        break;
00386     case SEC_OID_PKCS7_DIGESTED_DATA:
00387        rv = NSS_CMSDigestedData_Decode_AfterData(
00388                                   p7dcx->content.digestedData);
00389        break;
00390     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00391        rv = NSS_CMSEncryptedData_Decode_AfterData(
00392                                    p7dcx->content.encryptedData);
00393        break;
00394     case SEC_OID_PKCS7_DATA:
00395        /* do nothing */
00396        break;
00397     default:
00398        rv = SECFailure;
00399        break;
00400     }
00401 done:
00402     return rv;
00403 }
00404 
00405 static SECStatus
00406 nss_cms_after_end(NSSCMSDecoderContext *p7dcx)
00407 {
00408     SECStatus rv = SECSuccess;
00409 
00410     switch (p7dcx->type) {
00411     case SEC_OID_PKCS7_SIGNED_DATA:
00412        if (p7dcx->content.signedData)
00413            rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData);
00414        break;
00415     case SEC_OID_PKCS7_ENVELOPED_DATA:
00416        if (p7dcx->content.envelopedData)
00417            rv = NSS_CMSEnvelopedData_Decode_AfterEnd(
00418                                       p7dcx->content.envelopedData);
00419        break;
00420     case SEC_OID_PKCS7_DIGESTED_DATA:
00421        if (p7dcx->content.digestedData)
00422            rv = NSS_CMSDigestedData_Decode_AfterEnd(
00423                                      p7dcx->content.digestedData);
00424        break;
00425     case SEC_OID_PKCS7_ENCRYPTED_DATA:
00426        if (p7dcx->content.encryptedData)
00427            rv = NSS_CMSEncryptedData_Decode_AfterEnd(
00428                                       p7dcx->content.encryptedData);
00429        break;
00430     case SEC_OID_PKCS7_DATA:
00431        break;
00432     default:
00433        rv = SECFailure;     /* we should not have got that far... */
00434        break;
00435     }
00436     return rv;
00437 }
00438 
00439 /*
00440  * nss_cms_decoder_work_data - handle decoded data bytes.
00441  *
00442  * This function either decrypts the data if needed, and/or calculates digests
00443  * on it, then either stores it or passes it on to the next level decoder.
00444  */
00445 static void
00446 nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 
00447                           const unsigned char *data, unsigned long len,
00448                           PRBool final)
00449 {
00450     NSSCMSContentInfo *cinfo;
00451     unsigned char *buf = NULL;
00452     unsigned char *dest;
00453     unsigned int offset;
00454     SECStatus rv;
00455 
00456     /*
00457      * We should really have data to process, or we should be trying
00458      * to finish/flush the last block.  (This is an overly paranoid
00459      * check since all callers are in this file and simple inspection
00460      * proves they do it right.  But it could find a bug in future
00461      * modifications/development, that is why it is here.)
00462      */
00463     PORT_Assert ((data != NULL && len) || final);
00464 
00465     cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
00466 
00467     if (cinfo->ciphcx != NULL) {
00468        /*
00469         * we are decrypting.
00470         * 
00471         * XXX If we get an error, we do not want to do the digest or callback,
00472         * but we want to keep decoding.  Or maybe we want to stop decoding
00473         * altogether if there is a callback, because obviously we are not
00474         * sending the data back and they want to know that.
00475         */
00476 
00477        unsigned int outlen = 0;    /* length of decrypted data */
00478        unsigned int buflen;        /* length available for decrypted data */
00479 
00480        /* find out about the length of decrypted data */
00481        buflen = NSS_CMSCipherContext_DecryptLength(cinfo->ciphcx, len, final);
00482 
00483        /*
00484         * it might happen that we did not provide enough data for a full
00485         * block (decryption unit), and that there is no output available
00486         */
00487 
00488        /* no output available, AND no input? */
00489        if (buflen == 0 && len == 0)
00490            goto loser;      /* bail out */
00491 
00492        /*
00493         * have inner decoder: pass the data on (means inner content type is NOT data)
00494         * no inner decoder: we have DATA in here: either call callback or store
00495         */
00496        if (buflen != 0) {
00497            /* there will be some output - need to make room for it */
00498            /* allocate buffer from the heap */
00499            buf = (unsigned char *)PORT_Alloc(buflen);
00500            if (buf == NULL) {
00501               p7dcx->error = SEC_ERROR_NO_MEMORY;
00502               goto loser;
00503            }
00504        }
00505 
00506        /*
00507         * decrypt incoming data
00508         * buf can still be NULL here (and buflen == 0) here if we don't expect
00509         * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to
00510         * keep track of incoming data
00511         */
00512        rv = NSS_CMSCipherContext_Decrypt(cinfo->ciphcx, buf, &outlen, buflen,
00513                             data, len, final);
00514        if (rv != SECSuccess) {
00515            p7dcx->error = PORT_GetError();
00516            goto loser;
00517        }
00518 
00519        PORT_Assert (final || outlen == buflen);
00520        
00521        /* swap decrypted data in */
00522        data = buf;
00523        len = outlen;
00524     }
00525 
00526     if (len == 0)
00527        goto done;           /* nothing more to do */
00528 
00529     /*
00530      * Update the running digests with plaintext bytes (if we need to).
00531      */
00532     if (cinfo->digcx)
00533        NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
00534 
00535     /* at this point, we have the plain decoded & decrypted data 
00536     ** which is either more encoded DER (which we need to hand to the child 
00537     ** decoder) or data we need to hand back to our caller 
00538     */
00539 
00540     /* pass the content back to our caller or */
00541     /* feed our freshly decrypted and decoded data into child decoder */
00542     if (p7dcx->cb != NULL) {
00543        (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
00544     }
00545 #if 1
00546     else
00547 #endif
00548     if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
00549        /* store it in "inner" data item as well */
00550        /* find the DATA item in the encapsulated cinfo and store it there */
00551        NSSCMSDecoderData *decoderData = 
00552                             (NSSCMSDecoderData *)cinfo->content.pointer;
00553        SECItem *dataItem = &decoderData->data;
00554 
00555        offset = dataItem->len;
00556        if (dataItem->len+len > decoderData->totalBufferSize) {
00557            int needLen = (dataItem->len+len) * 2;
00558            dest = (unsigned char *)
00559                             PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen);
00560            if (dest == NULL) {
00561               p7dcx->error = SEC_ERROR_NO_MEMORY;
00562               goto loser;
00563            }
00564 
00565            if (dataItem->len) {
00566               PORT_Memcpy(dest, dataItem->data, dataItem->len);
00567            }
00568            decoderData->totalBufferSize = needLen;
00569            dataItem->data = dest;
00570        }
00571 
00572        /* copy it in */
00573        PORT_Memcpy(dataItem->data + offset, data, len);
00574        dataItem->len += len;
00575     }
00576 
00577 done:
00578 loser:
00579     if (buf)
00580        PORT_Free (buf);
00581 }
00582 
00583 /*
00584  * nss_cms_decoder_update_filter - process ASN.1 data
00585  *
00586  * once we have set up a filter in nss_cms_decoder_notify(),
00587  * all data processed by the ASN.1 decoder is also passed through here.
00588  * we pass the content bytes (as opposed to length and tag bytes) on to
00589  * nss_cms_decoder_work_data().
00590  */
00591 static void
00592 nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
00593                        int depth, SEC_ASN1EncodingPart data_kind)
00594 {
00595     NSSCMSDecoderContext *p7dcx;
00596 
00597     PORT_Assert (len);      /* paranoia */
00598     if (len == 0)
00599        return;
00600 
00601     p7dcx = (NSSCMSDecoderContext*)arg;
00602 
00603     p7dcx->saw_contents = PR_TRUE;
00604 
00605     /* pass on the content bytes only */
00606     if (data_kind == SEC_ASN1_Contents)
00607        nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, 
00608                                  PR_FALSE);
00609 }
00610 
00611 /*
00612  * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
00613  *
00614  * "poolp" - pointer to arena for message, or NULL if new pool should be created
00615  * "cb", "cb_arg" - callback function and argument for delivery of inner content
00616  * "pwfn", pwfn_arg" - callback function for getting token password
00617  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
00618  */
00619 NSSCMSDecoderContext *
00620 NSS_CMSDecoder_Start(PRArenaPool *poolp,
00621                     NSSCMSContentCallback cb, void *cb_arg,
00622                     PK11PasswordFunc pwfn, void *pwfn_arg,
00623                     NSSCMSGetDecryptKeyCallback decrypt_key_cb, 
00624                     void *decrypt_key_cb_arg)
00625 {
00626     NSSCMSDecoderContext *p7dcx;
00627     NSSCMSMessage *cmsg;
00628 
00629     cmsg = NSS_CMSMessage_Create(poolp);
00630     if (cmsg == NULL)
00631        return NULL;
00632 
00633     NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, 
00634                                      decrypt_key_cb_arg, NULL, NULL);
00635 
00636     p7dcx = PORT_ZNew(NSSCMSDecoderContext);
00637     if (p7dcx == NULL) {
00638        NSS_CMSMessage_Destroy(cmsg);
00639        return NULL;
00640     }
00641 
00642     p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate);
00643     if (p7dcx->dcx == NULL) {
00644        PORT_Free (p7dcx);
00645        NSS_CMSMessage_Destroy(cmsg);
00646        return NULL;
00647     }
00648 
00649     SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
00650 
00651     p7dcx->cmsg = cmsg;
00652     p7dcx->type = SEC_OID_UNKNOWN;
00653 
00654     p7dcx->cb = cb;
00655     p7dcx->cb_arg = cb_arg;
00656 
00657     return p7dcx;
00658 }
00659 
00660 /*
00661  * NSS_CMSDecoder_Update - feed DER-encoded data to decoder
00662  */
00663 SECStatus
00664 NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, 
00665                       unsigned long len)
00666 {
00667     SECStatus rv;
00668     if (p7dcx->dcx != NULL && p7dcx->error == 0) {      
00669        /* if error is set already, don't bother */
00670        rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len);
00671        if (rv != SECSuccess) {
00672            p7dcx->error = PORT_GetError();
00673            PORT_Assert (p7dcx->error);
00674            if (p7dcx->error == 0)
00675               p7dcx->error = -1;
00676        }
00677     }
00678 
00679     if (p7dcx->error == 0)
00680        return SECSuccess;
00681 
00682     /* there has been a problem, let's finish the decoder */
00683     if (p7dcx->dcx != NULL) {
00684        (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
00685        p7dcx->dcx = NULL;
00686     }
00687     PORT_SetError (p7dcx->error);
00688 
00689     return SECFailure;
00690 }
00691 
00692 /*
00693  * NSS_CMSDecoder_Cancel - stop decoding in case of error
00694  */
00695 void
00696 NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx)
00697 {
00698     /* XXXX what about inner decoders? running digests? decryption? */
00699     /* XXXX there's a leak here! */
00700     NSS_CMSMessage_Destroy(p7dcx->cmsg);
00701     (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
00702     PORT_Free(p7dcx);
00703 }
00704 
00705 /*
00706  * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
00707  */
00708 NSSCMSMessage *
00709 NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx)
00710 {
00711     NSSCMSMessage *cmsg;
00712 
00713     cmsg = p7dcx->cmsg;
00714 
00715     if (p7dcx->dcx == NULL || 
00716         SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
00717        nss_cms_after_end(p7dcx) != SECSuccess)
00718     {
00719        NSS_CMSMessage_Destroy(cmsg);      /* get rid of pool if it's ours */
00720        cmsg = NULL;
00721     }
00722 
00723     PORT_Free(p7dcx);
00724     return cmsg;
00725 }
00726 
00727 NSSCMSMessage *
00728 NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
00729                   NSSCMSContentCallback cb, void *cb_arg,
00730                   PK11PasswordFunc pwfn, void *pwfn_arg,
00731                   NSSCMSGetDecryptKeyCallback decrypt_key_cb, 
00732                   void *decrypt_key_cb_arg)
00733 {
00734     NSSCMSDecoderContext *p7dcx;
00735 
00736     /* first arg(poolp) == NULL => create our own pool */
00737     p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, 
00738                                  decrypt_key_cb, decrypt_key_cb_arg);
00739     NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len);
00740     return NSS_CMSDecoder_Finish(p7dcx);
00741 }
00742