2  * The contents of this file are subject to the Mozilla Public 
   3  * License Version 1.1 (the "License"); you may not use this file 
   4  * except in compliance with the License. You may obtain a copy of 
   5  * the License at http://www.mozilla.org/MPL/ 
   7  * Software distributed under the License is distributed on an "AS 
   8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 
   9  * implied. See the License for the specific language governing 
  10  * rights and limitations under the License. 
  12  * The Original Code is the Netscape security libraries. 
  14  * The Initial Developer of the Original Code is Netscape 
  15  * Communications Corporation.  Portions created by Netscape are  
  16  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All 
  21  * Alternatively, the contents of this file may be used under the 
  22  * terms of the GNU General Public License Version 2 or later (the 
  23  * "GPL"), in which case the provisions of the GPL are applicable  
  24  * instead of those above.  If you wish to allow use of your  
  25  * version of this file only under the terms of the GPL and not to 
  26  * allow others to use your version of this file under the MPL, 
  27  * indicate your decision by deleting the provisions above and 
  28  * replace them with the notice and other provisions required by 
  29  * the GPL.  If you do not delete the provisions above, a recipient 
  30  * may use your version of this file under either the MPL or the 
  38 #include <Security/SecCmsDecoder.h> 
  39 #include <Security/SecCmsContentInfo.h> 
  40 #include <Security/SecCmsDigestContext.h> 
  41 #include <Security/SecCmsMessage.h> 
  45 #include "SecAsn1Item.h" 
  48 #include <security_asn1/secasn1.h> 
  49 #include <security_asn1/secerr.h> 
  50 #include <security_asn1/secport.h> 
  54 struct SecCmsDecoderStr 
{ 
  55     SEC_ASN1DecoderContext 
*            dcx
;            /* ASN.1 decoder context */ 
  56     SecCmsMessageRef                    cmsg
;           /* backpointer to the root message */ 
  57     SECOidTag                           type
;           /* type of message */ 
  58     SecCmsContent                       content
;        /* pointer to message */ 
  59     SecCmsDecoderRef                    childp7dcx
;     /* inner CMS decoder context */ 
  62     SecCmsContentCallback               cb
; 
  66 /* We use size_t for len in this function because the SEC_ASN1Decoder* layer 
  67    uses that for callback function */ 
  68 static void nss_cms_decoder_update_filter (void *arg
, const char *data
, size_t len
, 
  69                           int depth
, SEC_ASN1EncodingPart data_kind
); 
  70 static OSStatus 
nss_cms_before_data(SecCmsDecoderRef p7dcx
); 
  71 static OSStatus 
nss_cms_after_data(SecCmsDecoderRef p7dcx
); 
  72 static OSStatus 
nss_cms_after_end(SecCmsDecoderRef p7dcx
); 
  73 static void nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx
,  
  74                              const unsigned char *data
, size_t len
, Boolean final
); 
  76 extern const SecAsn1Template SecCmsMessageTemplate
[]; 
  79  * nss_cms_decoder_notify - 
  80  *  this is the driver of the decoding process. It gets called by the ASN.1 
  81  *  decoder before and after an object is decoded. 
  82  *  at various points in the decoding process, we intercept to set up and do 
  86 nss_cms_decoder_notify(void *arg
, Boolean before
, void *dest
, int depth
) 
  88     SecCmsDecoderRef p7dcx
; 
  89     SecCmsContentInfoRef rootcinfo
, cinfo
; 
  90     Boolean after 
= !before
; 
  92     p7dcx 
= (SecCmsDecoderRef
)arg
; 
  93     rootcinfo 
= &(p7dcx
->cmsg
->contentInfo
); 
  95     /* XXX error handling: need to set p7dcx->error */ 
  98     fprintf(stderr
, "%6.6s, dest = 0x%08x, depth = %d\n", before 
? "before" : "after", dest
, depth
); 
 101     /* so what are we working on right now? */ 
 102     switch (p7dcx
->type
) { 
 103     case SEC_OID_UNKNOWN
: 
 105          * right now, we are still decoding the OUTER (root) cinfo 
 106          * As soon as we know the inner content type, set up the info, 
 107          * but NO inner decoder or filter. The root decoder handles the first 
 108          * level children by itself - only for encapsulated contents (which 
 109          * are encoded as DER inside of an OCTET STRING) we need to set up a 
 112         if (after 
&& dest 
== &(rootcinfo
->contentType
)) { 
 113             p7dcx
->type 
= SecCmsContentInfoGetContentTypeTag(rootcinfo
); 
 114             p7dcx
->content 
= rootcinfo
->content
;        /* is this ready already ? need to alloc? */ 
 115             /* XXX yes we need to alloc -- continue here */ 
 118     case SEC_OID_PKCS7_DATA
: 
 119         /* this can only happen if the outermost cinfo has DATA in it */ 
 120         /* otherwise, we handle this type implicitely in the inner decoders */ 
 122         if (before 
&& dest 
== &(rootcinfo
->content
)) { 
 123             /* fake it to cause the filter to put the data in the right place... */ 
 124             /* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */ 
 125             SEC_ASN1DecoderSetFilterProc(p7dcx
->dcx
, 
 126                                           nss_cms_decoder_update_filter
, 
 128                                           (Boolean
)(p7dcx
->cb 
!= NULL
)); 
 132         if (after 
&& dest 
== &(rootcinfo
->content
.data
)) { 
 133             /* remove the filter */ 
 134             SEC_ASN1DecoderClearFilterProc(p7dcx
->dcx
); 
 138     case SEC_OID_PKCS7_SIGNED_DATA
: 
 139     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 140     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 141     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 143         if (before 
&& dest 
== &(rootcinfo
->content
)) 
 144             break;                                      /* we're not there yet */ 
 146         if (p7dcx
->content
.pointer 
== NULL
) 
 147             p7dcx
->content 
= rootcinfo
->content
; 
 149         /* get this data type's inner contentInfo */ 
 150         cinfo 
= SecCmsContentGetContentInfo(p7dcx
->content
.pointer
, p7dcx
->type
); 
 152         if (before 
&& dest 
== &(cinfo
->contentType
)) { 
 153             /* at this point, set up the &%$&$ back pointer */ 
 154             /* we cannot do it later, because the content itself is optional! */ 
 155             /* please give me C++ */ 
 156             switch (p7dcx
->type
) { 
 157             case SEC_OID_PKCS7_SIGNED_DATA
: 
 158                 p7dcx
->content
.signedData
->contentInfo
.cmsg 
= p7dcx
->cmsg
; 
 160             case SEC_OID_PKCS7_DIGESTED_DATA
: 
 161                 p7dcx
->content
.digestedData
->contentInfo
.cmsg 
= p7dcx
->cmsg
; 
 163             case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 164                 p7dcx
->content
.envelopedData
->contentInfo
.cmsg 
= p7dcx
->cmsg
; 
 166             case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 167                 p7dcx
->content
.encryptedData
->contentInfo
.cmsg 
= p7dcx
->cmsg
; 
 175         if (before 
&& dest 
== &(cinfo
->rawContent
)) { 
 176             /* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */ 
 177             SEC_ASN1DecoderSetFilterProc(p7dcx
->dcx
, nss_cms_decoder_update_filter
, 
 178                                           p7dcx
, (Boolean
)(p7dcx
->cb 
!= NULL
)); 
 181             /* we're right in front of the data */ 
 182             if (nss_cms_before_data(p7dcx
) != SECSuccess
) { 
 183                 SEC_ASN1DecoderClearFilterProc(p7dcx
->dcx
);     /* stop all processing */ 
 184                 p7dcx
->error 
= PORT_GetError(); 
 187         if (after 
&& dest 
== &(cinfo
->rawContent
)) { 
 188             /* we're right after of the data */ 
 189             if (nss_cms_after_data(p7dcx
) != SECSuccess
) 
 190                 p7dcx
->error 
= PORT_GetError(); 
 192             /* we don't need to see the contents anymore */ 
 193             SEC_ASN1DecoderClearFilterProc(p7dcx
->dcx
); 
 198     case SEC_OID_PKCS7_AUTHENTICATED_DATA
: 
 201         /* unsupported or unknown message type - fail (more or less) gracefully */ 
 202         p7dcx
->error 
= SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE
; 
 208  * nss_cms_before_data - set up the current encoder to receive data 
 211 nss_cms_before_data(SecCmsDecoderRef p7dcx
) 
 216     SecCmsDecoderRef childp7dcx
; 
 217     SecCmsContentInfoRef cinfo
; 
 218     const SecAsn1Template 
*template; 
 222     poolp 
= p7dcx
->cmsg
->poolp
; 
 224     /* call _Decode_BeforeData handlers */ 
 225     switch (p7dcx
->type
) { 
 226     case SEC_OID_PKCS7_SIGNED_DATA
: 
 227         /* we're decoding a signedData, so set up the digests */ 
 228         rv 
= SecCmsSignedDataDecodeBeforeData(p7dcx
->content
.signedData
); 
 229         if (rv 
!= SECSuccess
) 
 232     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 233         /* we're encoding a digestedData, so set up the digest */ 
 234         rv 
= SecCmsDigestedDataDecodeBeforeData(p7dcx
->content
.digestedData
); 
 235         if (rv 
!= SECSuccess
) 
 238     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 239         rv 
= SecCmsEnvelopedDataDecodeBeforeData(p7dcx
->content
.envelopedData
); 
 240         if (rv 
!= SECSuccess
) 
 243     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 244         rv 
= SecCmsEncryptedDataDecodeBeforeData(p7dcx
->content
.encryptedData
); 
 245         if (rv 
!= SECSuccess
) 
 252     /* ok, now we have a pointer to cinfo */ 
 253     /* find out what kind of data is encapsulated */ 
 255     cinfo 
= SecCmsContentGetContentInfo(p7dcx
->content
.pointer
, p7dcx
->type
); 
 256     childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 258     if (childtype 
== SEC_OID_PKCS7_DATA
) { 
 259         cinfo
->content
.data 
= SECITEM_AllocItem(poolp
, NULL
, 0); 
 260         if (cinfo
->content
.data 
== NULL
) 
 261             /* set memory error */ 
 264         p7dcx
->childp7dcx 
= NULL
; 
 268     /* set up inner decoder */ 
 270     if ((template = SecCmsUtilGetTemplateByTypeTag(childtype
)) == NULL
) 
 273     childp7dcx 
= (SecCmsDecoderRef
)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr
)); 
 274     if (childp7dcx 
== NULL
) 
 277     mark 
= PORT_ArenaMark(poolp
); 
 279     /* allocate space for the stuff we're creating */ 
 280     size 
= SecCmsUtilGetSizeByTypeTag(childtype
); 
 281     childp7dcx
->content
.pointer 
= (void *)PORT_ArenaZAlloc(poolp
, size
); 
 282     if (childp7dcx
->content
.pointer 
== NULL
) 
 285     /* start the child decoder */ 
 286     childp7dcx
->dcx 
= SEC_ASN1DecoderStart(poolp
, childp7dcx
->content
.pointer
, template, NULL
, 0); 
 287     if (childp7dcx
->dcx 
== NULL
) 
 290     /* the new decoder needs to notify, too */ 
 291     SEC_ASN1DecoderSetNotifyProc(childp7dcx
->dcx
, nss_cms_decoder_notify
, childp7dcx
); 
 293     /* tell the parent decoder that it needs to feed us the content data */ 
 294     p7dcx
->childp7dcx 
= childp7dcx
; 
 296     childp7dcx
->type 
= childtype
;       /* our type */ 
 298     childp7dcx
->cmsg 
= p7dcx
->cmsg
;     /* backpointer to root message */ 
 300     /* should the child decoder encounter real data, it needs to give it to the caller */ 
 301     childp7dcx
->cb 
= p7dcx
->cb
; 
 302     childp7dcx
->cb_arg 
= p7dcx
->cb_arg
; 
 304     /* now set up the parent to hand decoded data to the next level decoder */ 
 305     p7dcx
->cb 
= (SecCmsContentCallback
)SecCmsDecoderUpdate
; 
 306     p7dcx
->cb_arg 
= childp7dcx
; 
 308     PORT_ArenaUnmark(poolp
, mark
); 
 314         PORT_ArenaRelease(poolp
, mark
); 
 316         PORT_Free(childp7dcx
); 
 317     p7dcx
->childp7dcx 
= NULL
; 
 322 nss_cms_after_data(SecCmsDecoderRef p7dcx
) 
 324     SecCmsDecoderRef childp7dcx
; 
 325     OSStatus rv 
= SECFailure
; 
 327     /* Handle last block. This is necessary to flush out the last bytes 
 328      * of a possibly incomplete block */ 
 329     nss_cms_decoder_work_data(p7dcx
, NULL
, 0, PR_TRUE
); 
 331     /* finish any "inner" decoders - there's no more data coming... */ 
 332     if (p7dcx
->childp7dcx 
!= NULL
) { 
 333         childp7dcx 
= p7dcx
->childp7dcx
; 
 334         if (childp7dcx
->dcx 
!= NULL
) { 
 335             if (SEC_ASN1DecoderFinish(childp7dcx
->dcx
) != SECSuccess
) { 
 336                 /* do what? free content? */ 
 339                 rv 
= nss_cms_after_end(childp7dcx
); 
 341             if (rv 
!= SECSuccess
) 
 344         PORT_Free(p7dcx
->childp7dcx
); 
 345         p7dcx
->childp7dcx 
= NULL
; 
 348     switch (p7dcx
->type
) { 
 349     case SEC_OID_PKCS7_SIGNED_DATA
: 
 350         /* this will finish the digests and verify */ 
 351         rv 
= SecCmsSignedDataDecodeAfterData(p7dcx
->content
.signedData
); 
 353     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 354         rv 
= SecCmsEnvelopedDataDecodeAfterData(p7dcx
->content
.envelopedData
); 
 356     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 357         rv 
= SecCmsDigestedDataDecodeAfterData(p7dcx
->content
.digestedData
); 
 359     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 360         rv 
= SecCmsEncryptedDataDecodeAfterData(p7dcx
->content
.encryptedData
); 
 362     case SEC_OID_PKCS7_DATA
: 
 374 nss_cms_after_end(SecCmsDecoderRef p7dcx
) 
 379     poolp 
= p7dcx
->cmsg
->poolp
; 
 381     switch (p7dcx
->type
) { 
 382     case SEC_OID_PKCS7_SIGNED_DATA
: 
 383         rv 
= SecCmsSignedDataDecodeAfterEnd(p7dcx
->content
.signedData
); 
 385     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 386         rv 
= SecCmsEnvelopedDataDecodeAfterEnd(p7dcx
->content
.envelopedData
); 
 388     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 389         rv 
= SecCmsDigestedDataDecodeAfterEnd(p7dcx
->content
.digestedData
); 
 391     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 392         rv 
= SecCmsEncryptedDataDecodeAfterEnd(p7dcx
->content
.encryptedData
); 
 394     case SEC_OID_PKCS7_DATA
: 
 398         rv 
= SECFailure
;        /* we should not have got that far... */ 
 405  * nss_cms_decoder_work_data - handle decoded data bytes. 
 407  * This function either decrypts the data if needed, and/or calculates digests 
 408  * on it, then either stores it or passes it on to the next level decoder. 
 411 nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx
,  
 412                              const unsigned char *data
, size_t len
, 
 415     SecCmsContentInfoRef cinfo
; 
 416     unsigned char *buf 
= NULL
; 
 420     SecAsn1Item 
* storage
; 
 423      * We should really have data to process, or we should be trying 
 424      * to finish/flush the last block.  (This is an overly paranoid 
 425      * check since all callers are in this file and simple inspection 
 426      * proves they do it right.  But it could find a bug in future 
 427      * modifications/development, that is why it is here.) 
 429     PORT_Assert ((data 
!= NULL 
&& len
) || final
); 
 430     /* Debug check for 64 bits cast later */ 
 431     PORT_Assert (len 
<= UINT_MAX
); 
 433     if (!p7dcx
->content
.pointer
)        // might be ExContent?? 
 436     cinfo 
= SecCmsContentGetContentInfo(p7dcx
->content
.pointer
, p7dcx
->type
); 
 438     if (cinfo
->ciphcx 
!= NULL
) { 
 442          * XXX If we get an error, we do not want to do the digest or callback, 
 443          * but we want to keep decoding.  Or maybe we want to stop decoding 
 444          * altogether if there is a callback, because obviously we are not 
 445          * sending the data back and they want to know that. 
 448         unsigned int outlen 
= 0;        /* length of decrypted data */ 
 449         unsigned int buflen
;            /* length available for decrypted data */ 
 451         /* find out about the length of decrypted data */ 
 452         /* 64 bits cast: Worst case here is we may not decrypt the full CMS blob, if the blob is bigger than 4GB */ 
 453         buflen 
= SecCmsCipherContextDecryptLength(cinfo
->ciphcx
, (unsigned int)len
, final
); 
 456          * it might happen that we did not provide enough data for a full 
 457          * block (decryption unit), and that there is no output available 
 460         /* no output available, AND no input? */ 
 461         if (buflen 
== 0 && len 
== 0) 
 462             goto loser
; /* bail out */ 
 465          * have inner decoder: pass the data on (means inner content type is NOT data) 
 466          * no inner decoder: we have DATA in here: either call callback or store 
 469             /* there will be some output - need to make room for it */ 
 470             /* allocate buffer from the heap */ 
 471             buf 
= (unsigned char *)PORT_Alloc(buflen
); 
 473                 p7dcx
->error 
= SEC_ERROR_NO_MEMORY
; 
 479          * decrypt incoming data 
 480          * buf can still be NULL here (and buflen == 0) here if we don't expect 
 481          * any output (see above), but we still need to call SecCmsCipherContextDecrypt to 
 482          * keep track of incoming data 
 484         rv 
= SecCmsCipherContextDecrypt(cinfo
->ciphcx
, buf
, &outlen
, buflen
, 
 485                                data
, (unsigned int)len
, final
); 
 486         if (rv 
!= SECSuccess
) { 
 487             p7dcx
->error 
= PORT_GetError(); 
 491         //PORT_Assert (final || outlen == buflen); 
 493         /* swap decrypted data in */ 
 499         goto done
;              /* nothing more to do */ 
 502      * Update the running digests with plaintext bytes (if we need to). 
 505         SecCmsDigestContextUpdate(cinfo
->digcx
, data
, len
); 
 507     /* at this point, we have the plain decoded & decrypted data */ 
 508     /* which is either more encoded DER which we need to hand to the child decoder */ 
 509     /*              or data we need to hand back to our caller */ 
 511     /* pass the content back to our caller or */ 
 512     /* feed our freshly decrypted and decoded data into child decoder */ 
 513     if (p7dcx
->cb 
!= NULL
) { 
 514         (*p7dcx
->cb
)(p7dcx
->cb_arg
, (const char *)data
, len
); 
 519     if (SecCmsContentInfoGetContentTypeTag(cinfo
) == SEC_OID_PKCS7_DATA
) { 
 520         /* store it in "inner" data item as well */ 
 521         /* find the DATA item in the encapsulated cinfo and store it there */ 
 522         storage 
= cinfo
->content
.data
; 
 524         offset 
= storage
->Length
; 
 526         /* check for potential overflow */ 
 527         if (len 
>= (size_t)(INT_MAX 
- storage
->Length
)) { 
 528           p7dcx
->error 
= SEC_ERROR_NO_MEMORY
; 
 532         if (storage
->Length 
== 0) { 
 533             dest 
= (unsigned char *)PORT_ArenaAlloc(p7dcx
->cmsg
->poolp
, len
); 
 535             dest 
= (unsigned char *)PORT_ArenaGrow(p7dcx
->cmsg
->poolp
,  
 538                                   storage
->Length 
+ len
); 
 541             p7dcx
->error 
= SEC_ERROR_NO_MEMORY
; 
 545         storage
->Data 
= dest
; 
 546         storage
->Length 
+= len
; 
 549         PORT_Memcpy(storage
->Data 
+ offset
, data
, len
); 
 559  * nss_cms_decoder_update_filter - process ASN.1 data 
 561  * once we have set up a filter in nss_cms_decoder_notify(), 
 562  * all data processed by the ASN.1 decoder is also passed through here. 
 563  * we pass the content bytes (as opposed to length and tag bytes) on to 
 564  * nss_cms_decoder_work_data(). 
 566  * len has to be of type size_t because it is a callback to the 
 567  * SEC_ASN1Decoder layer 
 570 nss_cms_decoder_update_filter (void *arg
, const char *data
, size_t len
, 
 571                           int depth
, SEC_ASN1EncodingPart data_kind
) 
 573     SecCmsDecoderRef p7dcx
; 
 575     PORT_Assert (len
);  /* paranoia */ 
 579     p7dcx 
= (SecCmsDecoderRef
)arg
; 
 581     p7dcx
->saw_contents 
= PR_TRUE
; 
 583     /* pass on the content bytes only */ 
 584     if (data_kind 
== SEC_ASN1_Contents
) 
 585         nss_cms_decoder_work_data(p7dcx
, (const unsigned char *) data
, len
, PR_FALSE
); 
 589  * SecCmsDecoderCreate - set up decoding of a BER-encoded CMS message 
 592 SecCmsDecoderCreate(SecCmsContentCallback cb
, void *cb_arg
, 
 593                     PK11PasswordFunc pwfn
, void *pwfn_arg
, 
 594                     SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
, 
 595                     SecCmsDecoderRef 
*outDecoder
) 
 597     SecCmsDecoderRef p7dcx
; 
 598     SecCmsMessageRef cmsg
; 
 601     cmsg 
= SecCmsMessageCreate(); 
 605     SecCmsMessageSetEncodingParams(cmsg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
); 
 607     p7dcx 
= (SecCmsDecoderRef
)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr
)); 
 609         SecCmsMessageDestroy(cmsg
); 
 613     p7dcx
->dcx 
= SEC_ASN1DecoderStart(cmsg
->poolp
, cmsg
, SecCmsMessageTemplate
, NULL
, 0); 
 614     if (p7dcx
->dcx 
== NULL
) { 
 616         SecCmsMessageDestroy(cmsg
); 
 620     SEC_ASN1DecoderSetNotifyProc (p7dcx
->dcx
, nss_cms_decoder_notify
, p7dcx
); 
 623     p7dcx
->type 
= SEC_OID_UNKNOWN
; 
 626     p7dcx
->cb_arg 
= cb_arg
; 
 629     return errSecSuccess
; 
 632     result 
= PORT_GetError(); 
 637  * SecCmsDecoderUpdate - feed DER-encoded data to decoder 
 640 SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx
, const void *buf
, CFIndex len
) 
 642     if (p7dcx
->dcx 
!= NULL 
&& p7dcx
->error 
== 0) {      /* if error is set already, don't bother */ 
 643         if (SEC_ASN1DecoderUpdate (p7dcx
->dcx
, buf
, len
) != SECSuccess
) { 
 644             p7dcx
->error 
= PORT_GetError(); 
 645             PORT_Assert (p7dcx
->error
); 
 646             if (p7dcx
->error 
== 0) 
 651     if (p7dcx
->error 
== 0) 
 654     /* there has been a problem, let's finish the decoder */ 
 655     if (p7dcx
->dcx 
!= NULL
) { 
 656         /* @@@ Change this to SEC_ASN1DecoderAbort()? */ 
 657         (void) SEC_ASN1DecoderFinish (p7dcx
->dcx
); 
 660     PORT_SetError (p7dcx
->error
); 
 666  * SecCmsDecoderDestroy - stop decoding in case of error 
 669 SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx
) 
 671     /* XXXX what about inner decoders? running digests? decryption? */ 
 672     /* XXXX there's a leak here! */ 
 673     SecCmsMessageDestroy(p7dcx
->cmsg
); 
 675         (void)SEC_ASN1DecoderFinish(p7dcx
->dcx
); 
 680  * SecCmsDecoderFinish - mark the end of inner content and finish decoding 
 683 SecCmsDecoderFinish(SecCmsDecoderRef p7dcx
, SecCmsMessageRef 
*outMessage
) 
 685     SecCmsMessageRef cmsg
; 
 690     if (p7dcx
->dcx 
== NULL 
|| SEC_ASN1DecoderFinish(p7dcx
->dcx
) != SECSuccess 
|| 
 691         nss_cms_after_end(p7dcx
) != SECSuccess
) 
 693         SecCmsMessageDestroy(cmsg
); 
 694         result 
= PORT_GetError(); 
 699     result 
= errSecSuccess
; 
 707 SecCmsMessageDecode(const SecAsn1Item 
*encodedMessage
, 
 708                     SecCmsContentCallback cb
, void *cb_arg
, 
 709                     PK11PasswordFunc pwfn
, void *pwfn_arg
, 
 710                     SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
, 
 711                     SecCmsMessageRef 
*outMessage
) 
 714     SecCmsDecoderRef decoder 
= NULL
; 
 716     result 
= SecCmsDecoderCreate(cb
, cb_arg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
, &decoder
); 
 719     result 
= SecCmsDecoderUpdate(decoder
, encodedMessage
->Data
, encodedMessage
->Length
); 
 721         SecCmsDecoderDestroy(decoder
); 
 725     result 
= SecCmsDecoderFinish(decoder
, outMessage
);