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/SecCmsEncoder.h> 
  39 #include <Security/SecCmsContentInfo.h> 
  40 #include <Security/SecCmsDigestContext.h> 
  41 #include <Security/SecCmsMessage.h> 
  46 #include "SecAsn1Item.h" 
  48 #include <security_asn1/secasn1.h> 
  49 #include <security_asn1/secerr.h> 
  50 #include <security_asn1/secport.h> 
  52 #include <Security/SecBase.h> 
  56 struct nss_cms_encoder_output 
{ 
  57     SecCmsContentCallback outputfn
; 
  59     CFMutableDataRef berData
; 
  62 struct SecCmsEncoderStr 
{ 
  63     SEC_ASN1EncoderContext 
*    ecx
;            /* ASN.1 encoder context */ 
  64     Boolean                     ecxupdated
;     /* true if data was handed in */ 
  65     SecCmsMessageRef            cmsg
;           /* pointer to the root message */ 
  66     SECOidTag                   type
;           /* type tag of the current content */ 
  67     SecCmsContent               content
;        /* pointer to current content */ 
  68     struct nss_cms_encoder_output output
;       /* output function */ 
  69     int                         error
;          /* error code */ 
  70     SecCmsEncoderRef    childp7ecx
;     /* link to child encoder context */ 
  73 static OSStatus 
nss_cms_before_data(SecCmsEncoderRef p7ecx
); 
  74 static OSStatus 
nss_cms_after_data(SecCmsEncoderRef p7ecx
); 
  75 static void nss_cms_encoder_update(void *arg
, const char *data
, size_t len
); 
  76 static OSStatus 
nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, SecAsn1Item 
* dest
, 
  77                              const unsigned char *data
, size_t len
, 
  78                              Boolean final
, Boolean innermost
); 
  80 extern const SecAsn1Template SecCmsMessageTemplate
[]; 
  83  * The little output function that the ASN.1 encoder calls to hand 
  84  * us bytes which we in turn hand back to our caller (via the callback 
  88 nss_cms_encoder_out(void *arg
, const char *buf
, size_t len
, 
  89                       int depth
, SEC_ASN1EncodingPart data_kind
) 
  91     struct nss_cms_encoder_output 
*output 
= (struct nss_cms_encoder_output 
*)arg
; 
  96     fprintf(stderr
, "kind = %d, depth = %d, len = %d\n", data_kind
, depth
, len
); 
  97     for (i
=0; i 
< len
; i
++) { 
  98         fprintf(stderr
, " %02x%s", (unsigned int)buf
[i
] & 0xff, ((i 
% 16) == 15) ? "\n" : ""); 
 101         fprintf(stderr
, "\n"); 
 104     if (output
->outputfn 
!= NULL
) 
 105         /* call output callback with DER data */ 
 106         output
->outputfn(output
->outputarg
, buf
, len
); 
 108     if (output
->berData 
!= NULL
) { 
 109         /* store DER data in output->dest */ 
 110         CFDataAppendBytes(output
->berData
, (const UInt8 
*)buf
, len
); 
 115  * nss_cms_encoder_notify - ASN.1 encoder callback 
 117  * this function is called by the ASN.1 encoder before and after the encoding of 
 118  * every object. here, it is used to keep track of data structures, set up 
 119  * encryption and/or digesting and possibly set up child encoders. 
 122 nss_cms_encoder_notify(void *arg
, Boolean before
, void *dest
, int depth
) 
 124     SecCmsEncoderRef p7ecx
; 
 125     SecCmsContentInfoRef rootcinfo
, cinfo
; 
 126     Boolean after 
= !before
; 
 130     p7ecx 
= (SecCmsEncoderRef
)arg
; 
 131     PORT_Assert(p7ecx 
!= NULL
); 
 133     rootcinfo 
= &(p7ecx
->cmsg
->contentInfo
); 
 136     fprintf(stderr
, "%6.6s, dest = 0x%08x, depth = %d\n", before 
? "before" : "after", dest
, depth
); 
 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. 
 143     switch (p7ecx
->type
) { 
 145     case SEC_OID_UNKNOWN
: 
 146         /* we're still in the root message */ 
 147         if (after 
&& dest 
== &(rootcinfo
->contentType
)) { 
 148             /* got the content type OID now - so find out the type tag */ 
 149             p7ecx
->type 
= SecCmsContentInfoGetContentTypeTag(rootcinfo
); 
 150             /* set up a pointer to our current content */ 
 151             p7ecx
->content 
= rootcinfo
->content
; 
 155     case SEC_OID_PKCS7_DATA
: 
 156         if (before 
&& dest 
== &(rootcinfo
->rawContent
)) { 
 157             /* just set up encoder to grab from user - no encryption or digesting */ 
 158             if ((item 
= rootcinfo
->content
.data
) != NULL
) 
 159                 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
); 
 161                 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
); 
 162             SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */ 
 166     case SEC_OID_PKCS7_SIGNED_DATA
: 
 167     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 168     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 169     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 171         /* when we know what the content is, we encode happily until we reach the inner content */ 
 172         cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 173         childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 175         if (after 
&& dest 
== &(cinfo
->contentType
)) { 
 176             /* we're right before encoding the data (if we have some or not) */ 
 177             /* (for encrypted data, we're right before the contentEncAlg which may change */ 
 178             /*  in nss_cms_before_data because of IV calculation when setting up encryption) */ 
 179             if (nss_cms_before_data(p7ecx
) != SECSuccess
) 
 180                 p7ecx
->error 
= PORT_GetError(); 
 182         if (before 
&& dest 
== &(cinfo
->rawContent
)) { 
 183             if (childtype 
== SEC_OID_PKCS7_DATA 
&& (item 
= cinfo
->content
.data
) != NULL
) 
 184                 /* we have data - feed it in */ 
 185                 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
); 
 187                 /* else try to get it from user */ 
 188                 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
); 
 190         if (after 
&& dest 
== &(cinfo
->rawContent
)) { 
 191             if (nss_cms_after_data(p7ecx
) != SECSuccess
) 
 192                 p7ecx
->error 
= PORT_GetError(); 
 193             SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */ 
 200  * nss_cms_before_data - setup the current encoder to receive data 
 203 nss_cms_before_data(SecCmsEncoderRef p7ecx
) 
 207     SecCmsContentInfoRef cinfo
; 
 208     SecCmsEncoderRef childp7ecx
; 
 209     const SecAsn1Template 
*template; 
 211     /* call _Encode_BeforeData handlers */ 
 212     switch (p7ecx
->type
) { 
 213     case SEC_OID_PKCS7_SIGNED_DATA
: 
 214         /* we're encoding a signedData, so set up the digests */ 
 215         rv 
= SecCmsSignedDataEncodeBeforeData(p7ecx
->content
.signedData
); 
 217     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 218         /* we're encoding a digestedData, so set up the digest */ 
 219         rv 
= SecCmsDigestedDataEncodeBeforeData(p7ecx
->content
.digestedData
); 
 221     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 222         rv 
= SecCmsEnvelopedDataEncodeBeforeData(p7ecx
->content
.envelopedData
); 
 224     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 225         rv 
= SecCmsEncryptedDataEncodeBeforeData(p7ecx
->content
.encryptedData
); 
 230     if (rv 
!= SECSuccess
) 
 233     /* ok, now we have a pointer to cinfo */ 
 234     /* find out what kind of data is encapsulated */ 
 236     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 237     childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 240     case SEC_OID_PKCS7_SIGNED_DATA
: 
 241     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 242     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 243     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 245     case SEC_OID_PKCS7_DATA
:            /* XXX here also??? maybe yes! */ 
 247         /* in these cases, we need to set up a child encoder! */ 
 248         /* create new encoder context */ 
 249         childp7ecx 
= PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
)); 
 250         if (childp7ecx 
== NULL
) 
 253         /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder 
 254          * (which will encrypt and/or digest it) 
 255          * this needs to route back into our update function 
 256          * which finds the lowest encoding context & encrypts and computes digests */ 
 257         childp7ecx
->type 
= childtype
; 
 258         childp7ecx
->content 
= cinfo
->content
; 
 259         /* use the non-recursive update function here, of course */ 
 260         childp7ecx
->output
.outputfn 
= nss_cms_encoder_update
; 
 261         childp7ecx
->output
.outputarg 
= p7ecx
; 
 262         childp7ecx
->output
.berData 
= NULL
; 
 263         childp7ecx
->cmsg 
= p7ecx
->cmsg
; 
 265         template = SecCmsUtilGetTemplateByTypeTag(childtype
); 
 266         if (template == NULL
) 
 267             goto loser
;         /* cannot happen */ 
 269         /* now initialize the data for encoding the first third */ 
 270         switch (childp7ecx
->type
) { 
 271         case SEC_OID_PKCS7_SIGNED_DATA
: 
 272             rv 
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
); 
 274         case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 275             rv 
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
); 
 277         case SEC_OID_PKCS7_DIGESTED_DATA
: 
 278             rv 
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
); 
 280         case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 281             rv 
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
); 
 283         case SEC_OID_PKCS7_DATA
: 
 290         if (rv 
!= SECSuccess
) 
 294          * Initialize the BER encoder. 
 296         childp7ecx
->ecx 
= SEC_ASN1EncoderStart(cinfo
->content
.pointer
, template, 
 297                                            nss_cms_encoder_out
, &(childp7ecx
->output
)); 
 298         if (childp7ecx
->ecx 
== NULL
) 
 301         childp7ecx
->ecxupdated 
= PR_FALSE
; 
 304          * Indicate that we are streaming.  We will be streaming until we 
 305          * get past the contents bytes. 
 307         SEC_ASN1EncoderSetStreaming(childp7ecx
->ecx
); 
 310          * The notify function will watch for the contents field. 
 312         SEC_ASN1EncoderSetNotifyProc(childp7ecx
->ecx
, nss_cms_encoder_notify
, childp7ecx
); 
 314         /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ 
 315         /* encoding process - we'll do that from the update function instead */ 
 316         /* otherwise we'd be encoding data from a call of the notify function of the */ 
 317         /* parent encoder (which would not work) */ 
 319         /* this will kick off the encoding process & encode everything up to the content bytes, 
 320          * at which point the notify function sets streaming mode (and possibly creates 
 321          * another child encoder). */ 
 322         if (SEC_ASN1EncoderUpdate(childp7ecx
->ecx
, NULL
, 0) != SECSuccess
) 
 325         p7ecx
->childp7ecx 
= childp7ecx
; 
 328     case SEC_OID_PKCS7_DATA
: 
 329         p7ecx
->childp7ecx 
= NULL
; 
 332         /* we do not know this type */ 
 333         p7ecx
->error 
= SEC_ERROR_BAD_DER
; 
 342             SEC_ASN1EncoderFinish(childp7ecx
->ecx
); 
 343         PORT_Free(childp7ecx
); 
 349 nss_cms_after_data(SecCmsEncoderRef p7ecx
) 
 351     OSStatus rv 
= SECFailure
; 
 353     switch (p7ecx
->type
) { 
 354     case SEC_OID_PKCS7_SIGNED_DATA
: 
 355         /* this will finish the digests and sign */ 
 356         rv 
= SecCmsSignedDataEncodeAfterData(p7ecx
->content
.signedData
); 
 358     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 359         rv 
= SecCmsEnvelopedDataEncodeAfterData(p7ecx
->content
.envelopedData
); 
 361     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 362         rv 
= SecCmsDigestedDataEncodeAfterData(p7ecx
->content
.digestedData
); 
 364     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 365         rv 
= SecCmsEncryptedDataEncodeAfterData(p7ecx
->content
.encryptedData
); 
 367     case SEC_OID_PKCS7_DATA
: 
 378  * nss_cms_encoder_work_data - process incoming data 
 380  * (from the user or the next encoding layer) 
 381  * Here, we need to digest and/or encrypt, then pass it on 
 385 nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, SecAsn1Item 
* dest
, 
 386                              const unsigned char *data
, size_t len
, 
 387                              Boolean final
, Boolean innermost
) 
 389     unsigned char *buf 
= NULL
; 
 391     SecCmsContentInfoRef cinfo
; 
 393     rv 
= SECSuccess
;            /* may as well be optimistic */ 
 396      * We should really have data to process, or we should be trying 
 397      * to finish/flush the last block.  (This is an overly paranoid 
 398      * check since all callers are in this file and simple inspection 
 399      * proves they do it right.  But it could find a bug in future 
 400      * modifications/development, that is why it is here.) 
 402     PORT_Assert ((data 
!= NULL 
&& len
) || final
); 
 403     PORT_Assert (len 
< UINT_MAX
); /* overflow check for later cast */ 
 405     /* we got data (either from the caller, or from a lower level encoder) */ 
 406     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 408     /* Update the running digest. */ 
 409     if (len 
&& cinfo
->digcx 
!= NULL
) 
 410         SecCmsDigestContextUpdate(cinfo
->digcx
, data
, len
); 
 412     /* Encrypt this chunk. */ 
 413     if (cinfo
->ciphcx 
!= NULL
) { 
 414         unsigned int inlen
;     /* length of data being encrypted */ 
 415         unsigned int outlen 
= 0;        /* length of encrypted data */ 
 416         unsigned int buflen
;    /* length available for encrypted data */ 
 418         /* 64 bits cast: only an issue if unsigned int is smaller than size_t. 
 419            Worst case is you will truncate a CMS blob bigger than 4GB when 
 421         inlen 
= (unsigned int)len
; 
 423         buflen 
= SecCmsCipherContextEncryptLength(cinfo
->ciphcx
, inlen
, final
); 
 426              * No output is expected, but the input data may be buffered 
 427              * so we still have to call Encrypt. 
 429             rv 
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, NULL
, NULL
, 0, 
 439             buf 
= (unsigned char*)PORT_ArenaAlloc(p7ecx
->cmsg
->poolp
, buflen
); 
 441             buf 
= (unsigned char*)PORT_Alloc(buflen
); 
 446             rv 
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, buf
, &outlen
, buflen
, 
 451         if (rv 
!= SECSuccess
) 
 452             /* encryption or malloc failed? */ 
 458      * at this point (data,len) has everything we'd like to give to the CURRENT encoder 
 459      * (which will encode it, then hand it back to the user or the parent encoder) 
 460      * We don't encode the data if we're innermost and we're told not to include the data 
 462     if (p7ecx
->ecx 
!= NULL 
&& len 
&& (!innermost 
|| cinfo
->rawContent 
!= NULL
)) 
 463         rv 
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, (const char *)data
, len
); 
 467     if (cinfo
->ciphcx 
!= NULL
) { 
 471         } else if (buf 
!= NULL
) { 
 479  * nss_cms_encoder_update - deliver encoded data to the next higher level 
 481  * no recursion here because we REALLY want to end up at the next higher encoder! 
 484 nss_cms_encoder_update(void *arg
, const char *data
, size_t len
) 
 486     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */ 
 487     SecCmsEncoderRef p7ecx 
= (SecCmsEncoderRef
)arg
; 
 489     (void)nss_cms_encoder_work_data (p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_FALSE
); 
 493  * SecCmsEncoderCreate - set up encoding of a CMS message 
 495  * "cmsg" - message to encode 
 496  * "outputfn", "outputarg" - callback function for delivery of DER-encoded output 
 497  *                           will not be called if NULL. 
 498  * "dest" - if non-NULL, pointer to SecAsn1Item that will hold the DER-encoded output 
 499  * "destpoolp" - pool to allocate DER-encoded output in 
 500  * "pwfn", pwfn_arg" - callback function for getting token password 
 501  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData 
 502  * "detached_digestalgs", "detached_digests" - digests from detached content 
 505 SecCmsEncoderCreate(SecCmsMessageRef cmsg
, 
 506                     SecCmsContentCallback outputfn
, void *outputarg
, 
 507                     CFMutableDataRef outBer
, 
 508                     PK11PasswordFunc pwfn
, void *pwfn_arg
, 
 509                     SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
, 
 510                     SecCmsEncoderRef 
*outEncoder
) 
 512     SecCmsEncoderRef p7ecx
; 
 514     SecCmsContentInfoRef cinfo
; 
 516     SecCmsMessageSetEncodingParams(cmsg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
); 
 518     p7ecx 
= (SecCmsEncoderRef
)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
)); 
 520         result 
= errSecAllocate
; 
 525     p7ecx
->output
.outputfn 
= outputfn
; 
 526     p7ecx
->output
.outputarg 
= outputarg
; 
 527     p7ecx
->output
.berData 
= outBer
; 
 529     p7ecx
->type 
= SEC_OID_UNKNOWN
; 
 531     cinfo 
= SecCmsMessageGetContentInfo(cmsg
); 
 533     switch (SecCmsContentInfoGetContentTypeTag(cinfo
)) { 
 534     case SEC_OID_PKCS7_SIGNED_DATA
: 
 535         result 
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
); 
 537     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 538         result 
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
); 
 540     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 541         result 
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
); 
 543     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 544         result 
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
); 
 547         /* @@@ We need a better error for unsupported message types. */ 
 548         result 
= errSecParam
; 
 554     /* Initialize the BER encoder. 
 555      * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ 
 556     p7ecx
->ecx 
= SEC_ASN1EncoderStart(cmsg
, SecCmsMessageTemplate
, 
 557                                       nss_cms_encoder_out
, &(p7ecx
->output
)); 
 558     if (p7ecx
->ecx 
== NULL
) { 
 559         result 
= PORT_GetError(); 
 563     p7ecx
->ecxupdated 
= PR_FALSE
; 
 566      * Indicate that we are streaming.  We will be streaming until we 
 567      * get past the contents bytes. 
 569     SEC_ASN1EncoderSetStreaming(p7ecx
->ecx
); 
 572      * The notify function will watch for the contents field. 
 574     SEC_ASN1EncoderSetNotifyProc(p7ecx
->ecx
, nss_cms_encoder_notify
, p7ecx
); 
 576     /* this will kick off the encoding process & encode everything up to the content bytes, 
 577      * at which point the notify function sets streaming mode (and possibly creates 
 578      * a child encoder). */ 
 579     if (SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0) != SECSuccess
) { 
 580         result 
= PORT_GetError(); 
 591  * SecCmsEncoderUpdate - take content data delivery from the user 
 593  * "p7ecx" - encoder context 
 594  * "data" - content data 
 595  * "len" - length of content data 
 597  * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), 
 598  * then hand the data to the work_data fn 
 601 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx
, const void *data
, CFIndex len
) 
 604     SecCmsContentInfoRef cinfo
; 
 614     /* hand data to the innermost decoder */ 
 615     if (p7ecx
->childp7ecx
) { 
 617         result 
= SecCmsEncoderUpdate(p7ecx
->childp7ecx
, data
, len
); 
 619         /* we are at innermost decoder */ 
 620         /* find out about our inner content type - must be data */ 
 621         cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 622         childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 623         if (childtype 
!= SEC_OID_PKCS7_DATA
) 
 624             return errSecParam
; /* @@@ Maybe come up with a better error? */ 
 625         /* and we must not have preset data */ 
 626         if (cinfo
->content
.data 
!= NULL
) 
 627             return errSecParam
; /* @@@ Maybe come up with a better error? */ 
 629         /*  hand it the data so it can encode it (let DER trickle up the chain) */ 
 630         result 
= nss_cms_encoder_work_data(p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_TRUE
); 
 632             result 
= PORT_GetError(); 
 638  * SecCmsEncoderDestroy - stop all encoding 
 640  * we need to walk down the chain of encoders and the finish them from the innermost out 
 643 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx
) 
 645     /* XXX do this right! */ 
 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 SecCmsEncoderFinish, but that's allright. 
 653     if (p7ecx
->childp7ecx
) 
 654         SecCmsEncoderDestroy(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */ 
 657      * On the way back up, there will be no more data (if we had an 
 658      * inner encoder, it is done now!) 
 659      * Flush out any remaining data and/or finish digests. 
 661     if (nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx 
== NULL
))) 
 664     p7ecx
->childp7ecx 
= NULL
; 
 666     /* kick the encoder back into working mode again. 
 667      * We turn off streaming stuff (which will cause the encoder to continue 
 668      * encoding happily, now that we have all the data (like digests) ready for it). 
 670     SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
); 
 671     SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
); 
 673     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 
 674     SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0); 
 677     SEC_ASN1EncoderFinish(p7ecx
->ecx
); 
 682  * SecCmsEncoderFinish - signal the end of data 
 684  * we need to walk down the chain of encoders and the finish them from the innermost out 
 687 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx
) 
 690     SecCmsContentInfoRef cinfo
; 
 694      * Finish any inner decoders before us so that all the encoded data is flushed 
 695      * This basically finishes all the decoders from the innermost to the outermost. 
 696      * Finishing an inner decoder may result in data being updated to the outer decoder 
 697      * while we are already in SecCmsEncoderFinish, but that's allright. 
 699     if (p7ecx
->childp7ecx
) { 
 700         result 
= SecCmsEncoderFinish(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */ 
 706      * On the way back up, there will be no more data (if we had an 
 707      * inner encoder, it is done now!) 
 708      * Flush out any remaining data and/or finish digests. 
 710     result 
= nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx 
== NULL
)); 
 712         result 
= PORT_GetError(); 
 716     p7ecx
->childp7ecx 
= NULL
; 
 718     /* find out about our inner content type - must be data */ 
 719     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 720     childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 721     if (childtype 
== SEC_OID_PKCS7_DATA 
&& cinfo
->content
.data 
== NULL
) { 
 722         SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
); 
 723         /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 
 724         result 
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0); 
 726             result 
= PORT_GetError(); 
 729     SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
); 
 731     if (p7ecx
->error 
&& !result
) 
 732         result 
= p7ecx
->error
; 
 735     SEC_ASN1EncoderFinish(p7ecx
->ecx
); 
 741 SecCmsMessageEncode(SecCmsMessageRef cmsg
, const SecAsn1Item 
*input
, 
 742                     CFMutableDataRef outBer
) 
 744     SecCmsEncoderRef encoder 
= NULL
; 
 747     if (!cmsg 
|| !outBer
) { 
 748         result 
= errSecParam
; 
 752     result 
= SecCmsEncoderCreate(cmsg
, 0, 0, outBer
, 0, 0, 0, 0, &encoder
); 
 757         result 
= SecCmsEncoderUpdate(encoder
, input
->Data
, input
->Length
); 
 759             SecCmsEncoderDestroy(encoder
); 
 763     result 
= SecCmsEncoderFinish(encoder
);