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
; 
 131     p7ecx 
= (SecCmsEncoderRef
)arg
; 
 132     PORT_Assert(p7ecx 
!= NULL
); 
 134     rootcinfo 
= &(p7ecx
->cmsg
->contentInfo
); 
 135     poolp 
= p7ecx
->cmsg
->poolp
; 
 138     fprintf(stderr
, "%6.6s, dest = 0x%08x, depth = %d\n", before 
? "before" : "after", dest
, depth
); 
 142      * Watch for the content field, at which point we want to instruct 
 143      * the ASN.1 encoder to start taking bytes from the buffer. 
 145     switch (p7ecx
->type
) { 
 147     case SEC_OID_UNKNOWN
: 
 148         /* we're still in the root message */ 
 149         if (after 
&& dest 
== &(rootcinfo
->contentType
)) { 
 150             /* got the content type OID now - so find out the type tag */ 
 151             p7ecx
->type 
= SecCmsContentInfoGetContentTypeTag(rootcinfo
); 
 152             /* set up a pointer to our current content */ 
 153             p7ecx
->content 
= rootcinfo
->content
; 
 157     case SEC_OID_PKCS7_DATA
: 
 158         if (before 
&& dest 
== &(rootcinfo
->rawContent
)) { 
 159             /* just set up encoder to grab from user - no encryption or digesting */ 
 160             if ((item 
= rootcinfo
->content
.data
) != NULL
) 
 161                 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
); 
 163                 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
); 
 164             SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */ 
 168     case SEC_OID_PKCS7_SIGNED_DATA
: 
 169     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 170     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 171     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 173         /* when we know what the content is, we encode happily until we reach the inner content */ 
 174         cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 175         childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 177         if (after 
&& dest 
== &(cinfo
->contentType
)) { 
 178             /* we're right before encoding the data (if we have some or not) */ 
 179             /* (for encrypted data, we're right before the contentEncAlg which may change */ 
 180             /*  in nss_cms_before_data because of IV calculation when setting up encryption) */ 
 181             if (nss_cms_before_data(p7ecx
) != SECSuccess
) 
 182                 p7ecx
->error 
= PORT_GetError(); 
 184         if (before 
&& dest 
== &(cinfo
->rawContent
)) { 
 185             if (childtype 
== SEC_OID_PKCS7_DATA 
&& (item 
= cinfo
->content
.data
) != NULL
) 
 186                 /* we have data - feed it in */ 
 187                 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
); 
 189                 /* else try to get it from user */ 
 190                 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
); 
 192         if (after 
&& dest 
== &(cinfo
->rawContent
)) { 
 193             if (nss_cms_after_data(p7ecx
) != SECSuccess
) 
 194                 p7ecx
->error 
= PORT_GetError(); 
 195             SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */ 
 202  * nss_cms_before_data - setup the current encoder to receive data 
 205 nss_cms_before_data(SecCmsEncoderRef p7ecx
) 
 209     SecCmsContentInfoRef cinfo
; 
 211     SecCmsEncoderRef childp7ecx
; 
 212     const SecAsn1Template 
*template; 
 214     poolp 
= p7ecx
->cmsg
->poolp
; 
 216     /* call _Encode_BeforeData handlers */ 
 217     switch (p7ecx
->type
) { 
 218     case SEC_OID_PKCS7_SIGNED_DATA
: 
 219         /* we're encoding a signedData, so set up the digests */ 
 220         rv 
= SecCmsSignedDataEncodeBeforeData(p7ecx
->content
.signedData
); 
 222     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 223         /* we're encoding a digestedData, so set up the digest */ 
 224         rv 
= SecCmsDigestedDataEncodeBeforeData(p7ecx
->content
.digestedData
); 
 226     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 227         rv 
= SecCmsEnvelopedDataEncodeBeforeData(p7ecx
->content
.envelopedData
); 
 229     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 230         rv 
= SecCmsEncryptedDataEncodeBeforeData(p7ecx
->content
.encryptedData
); 
 235     if (rv 
!= SECSuccess
) 
 238     /* ok, now we have a pointer to cinfo */ 
 239     /* find out what kind of data is encapsulated */ 
 241     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 242     childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 245     case SEC_OID_PKCS7_SIGNED_DATA
: 
 246     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 247     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 248     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 250     case SEC_OID_PKCS7_DATA
:            /* XXX here also??? maybe yes! */ 
 252         /* in these cases, we need to set up a child encoder! */ 
 253         /* create new encoder context */ 
 254         childp7ecx 
= PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
)); 
 255         if (childp7ecx 
== NULL
) 
 258         /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder 
 259          * (which will encrypt and/or digest it) 
 260          * this needs to route back into our update function 
 261          * which finds the lowest encoding context & encrypts and computes digests */ 
 262         childp7ecx
->type 
= childtype
; 
 263         childp7ecx
->content 
= cinfo
->content
; 
 264         /* use the non-recursive update function here, of course */ 
 265         childp7ecx
->output
.outputfn 
= nss_cms_encoder_update
; 
 266         childp7ecx
->output
.outputarg 
= p7ecx
; 
 267         childp7ecx
->output
.berData 
= NULL
; 
 268         childp7ecx
->cmsg 
= p7ecx
->cmsg
; 
 270         template = SecCmsUtilGetTemplateByTypeTag(childtype
); 
 271         if (template == NULL
) 
 272             goto loser
;         /* cannot happen */ 
 274         /* now initialize the data for encoding the first third */ 
 275         switch (childp7ecx
->type
) { 
 276         case SEC_OID_PKCS7_SIGNED_DATA
: 
 277             rv 
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
); 
 279         case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 280             rv 
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
); 
 282         case SEC_OID_PKCS7_DIGESTED_DATA
: 
 283             rv 
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
); 
 285         case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 286             rv 
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
); 
 288         case SEC_OID_PKCS7_DATA
: 
 295         if (rv 
!= SECSuccess
) 
 299          * Initialize the BER encoder. 
 301         childp7ecx
->ecx 
= SEC_ASN1EncoderStart(cinfo
->content
.pointer
, template, 
 302                                            nss_cms_encoder_out
, &(childp7ecx
->output
)); 
 303         if (childp7ecx
->ecx 
== NULL
) 
 306         childp7ecx
->ecxupdated 
= PR_FALSE
; 
 309          * Indicate that we are streaming.  We will be streaming until we 
 310          * get past the contents bytes. 
 312         SEC_ASN1EncoderSetStreaming(childp7ecx
->ecx
); 
 315          * The notify function will watch for the contents field. 
 317         SEC_ASN1EncoderSetNotifyProc(childp7ecx
->ecx
, nss_cms_encoder_notify
, childp7ecx
); 
 319         /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ 
 320         /* encoding process - we'll do that from the update function instead */ 
 321         /* otherwise we'd be encoding data from a call of the notify function of the */ 
 322         /* parent encoder (which would not work) */ 
 324         /* this will kick off the encoding process & encode everything up to the content bytes, 
 325          * at which point the notify function sets streaming mode (and possibly creates 
 326          * another child encoder). */ 
 327         if (SEC_ASN1EncoderUpdate(childp7ecx
->ecx
, NULL
, 0) != SECSuccess
) 
 330         p7ecx
->childp7ecx 
= childp7ecx
; 
 333     case SEC_OID_PKCS7_DATA
: 
 334         p7ecx
->childp7ecx 
= NULL
; 
 337         /* we do not know this type */ 
 338         p7ecx
->error 
= SEC_ERROR_BAD_DER
; 
 347             SEC_ASN1EncoderFinish(childp7ecx
->ecx
); 
 348         PORT_Free(childp7ecx
); 
 354 nss_cms_after_data(SecCmsEncoderRef p7ecx
) 
 356     OSStatus rv 
= SECFailure
; 
 358     switch (p7ecx
->type
) { 
 359     case SEC_OID_PKCS7_SIGNED_DATA
: 
 360         /* this will finish the digests and sign */ 
 361         rv 
= SecCmsSignedDataEncodeAfterData(p7ecx
->content
.signedData
); 
 363     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 364         rv 
= SecCmsEnvelopedDataEncodeAfterData(p7ecx
->content
.envelopedData
); 
 366     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 367         rv 
= SecCmsDigestedDataEncodeAfterData(p7ecx
->content
.digestedData
); 
 369     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 370         rv 
= SecCmsEncryptedDataEncodeAfterData(p7ecx
->content
.encryptedData
); 
 372     case SEC_OID_PKCS7_DATA
: 
 383  * nss_cms_encoder_work_data - process incoming data 
 385  * (from the user or the next encoding layer) 
 386  * Here, we need to digest and/or encrypt, then pass it on 
 390 nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, SecAsn1Item 
* dest
, 
 391                              const unsigned char *data
, size_t len
, 
 392                              Boolean final
, Boolean innermost
) 
 394     unsigned char *buf 
= NULL
; 
 396     SecCmsContentInfoRef cinfo
; 
 398     rv 
= SECSuccess
;            /* may as well be optimistic */ 
 401      * We should really have data to process, or we should be trying 
 402      * to finish/flush the last block.  (This is an overly paranoid 
 403      * check since all callers are in this file and simple inspection 
 404      * proves they do it right.  But it could find a bug in future 
 405      * modifications/development, that is why it is here.) 
 407     PORT_Assert ((data 
!= NULL 
&& len
) || final
); 
 408     PORT_Assert (len 
< UINT_MAX
); /* overflow check for later cast */ 
 410     /* we got data (either from the caller, or from a lower level encoder) */ 
 411     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 413     /* Update the running digest. */ 
 414     if (len 
&& cinfo
->digcx 
!= NULL
) 
 415         SecCmsDigestContextUpdate(cinfo
->digcx
, data
, len
); 
 417     /* Encrypt this chunk. */ 
 418     if (cinfo
->ciphcx 
!= NULL
) { 
 419         unsigned int inlen
;     /* length of data being encrypted */ 
 420         unsigned int outlen 
= 0;        /* length of encrypted data */ 
 421         unsigned int buflen
;    /* length available for encrypted data */ 
 423         /* 64 bits cast: only an issue if unsigned int is smaller than size_t. 
 424            Worst case is you will truncate a CMS blob bigger than 4GB when 
 426         inlen 
= (unsigned int)len
; 
 428         buflen 
= SecCmsCipherContextEncryptLength(cinfo
->ciphcx
, inlen
, final
); 
 431              * No output is expected, but the input data may be buffered 
 432              * so we still have to call Encrypt. 
 434             rv 
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, NULL
, NULL
, 0, 
 444             buf 
= (unsigned char*)PORT_ArenaAlloc(p7ecx
->cmsg
->poolp
, buflen
); 
 446             buf 
= (unsigned char*)PORT_Alloc(buflen
); 
 451             rv 
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, buf
, &outlen
, buflen
, 
 456         if (rv 
!= SECSuccess
) 
 457             /* encryption or malloc failed? */ 
 463      * at this point (data,len) has everything we'd like to give to the CURRENT encoder 
 464      * (which will encode it, then hand it back to the user or the parent encoder) 
 465      * We don't encode the data if we're innermost and we're told not to include the data 
 467     if (p7ecx
->ecx 
!= NULL 
&& len 
&& (!innermost 
|| cinfo
->rawContent 
!= NULL
)) 
 468         rv 
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, (const char *)data
, len
); 
 472     if (cinfo
->ciphcx 
!= NULL
) { 
 476         } else if (buf 
!= NULL
) { 
 484  * nss_cms_encoder_update - deliver encoded data to the next higher level 
 486  * no recursion here because we REALLY want to end up at the next higher encoder! 
 489 nss_cms_encoder_update(void *arg
, const char *data
, size_t len
) 
 491     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */ 
 492     SecCmsEncoderRef p7ecx 
= (SecCmsEncoderRef
)arg
; 
 494     (void)nss_cms_encoder_work_data (p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_FALSE
); 
 498  * SecCmsEncoderCreate - set up encoding of a CMS message 
 500  * "cmsg" - message to encode 
 501  * "outputfn", "outputarg" - callback function for delivery of DER-encoded output 
 502  *                           will not be called if NULL. 
 503  * "dest" - if non-NULL, pointer to SecAsn1Item that will hold the DER-encoded output 
 504  * "destpoolp" - pool to allocate DER-encoded output in 
 505  * "pwfn", pwfn_arg" - callback function for getting token password 
 506  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData 
 507  * "detached_digestalgs", "detached_digests" - digests from detached content 
 510 SecCmsEncoderCreate(SecCmsMessageRef cmsg
, 
 511                     SecCmsContentCallback outputfn
, void *outputarg
, 
 512                     CFMutableDataRef outBer
, 
 513                     PK11PasswordFunc pwfn
, void *pwfn_arg
, 
 514                     SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
, 
 515                     SecCmsEncoderRef 
*outEncoder
) 
 517     SecCmsEncoderRef p7ecx
; 
 519     SecCmsContentInfoRef cinfo
; 
 521     SecCmsMessageSetEncodingParams(cmsg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
); 
 523     p7ecx 
= (SecCmsEncoderRef
)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
)); 
 525         result 
= errSecAllocate
; 
 530     p7ecx
->output
.outputfn 
= outputfn
; 
 531     p7ecx
->output
.outputarg 
= outputarg
; 
 532     p7ecx
->output
.berData 
= outBer
; 
 534     p7ecx
->type 
= SEC_OID_UNKNOWN
; 
 536     cinfo 
= SecCmsMessageGetContentInfo(cmsg
); 
 538     switch (SecCmsContentInfoGetContentTypeTag(cinfo
)) { 
 539     case SEC_OID_PKCS7_SIGNED_DATA
: 
 540         result 
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
); 
 542     case SEC_OID_PKCS7_ENVELOPED_DATA
: 
 543         result 
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
); 
 545     case SEC_OID_PKCS7_DIGESTED_DATA
: 
 546         result 
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
); 
 548     case SEC_OID_PKCS7_ENCRYPTED_DATA
: 
 549         result 
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
); 
 552         /* @@@ We need a better error for unsupported message types. */ 
 553         result 
= errSecParam
; 
 559     /* Initialize the BER encoder. 
 560      * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ 
 561     p7ecx
->ecx 
= SEC_ASN1EncoderStart(cmsg
, SecCmsMessageTemplate
, 
 562                                       nss_cms_encoder_out
, &(p7ecx
->output
)); 
 563     if (p7ecx
->ecx 
== NULL
) { 
 564         result 
= PORT_GetError(); 
 568     p7ecx
->ecxupdated 
= PR_FALSE
; 
 571      * Indicate that we are streaming.  We will be streaming until we 
 572      * get past the contents bytes. 
 574     SEC_ASN1EncoderSetStreaming(p7ecx
->ecx
); 
 577      * The notify function will watch for the contents field. 
 579     SEC_ASN1EncoderSetNotifyProc(p7ecx
->ecx
, nss_cms_encoder_notify
, p7ecx
); 
 581     /* this will kick off the encoding process & encode everything up to the content bytes, 
 582      * at which point the notify function sets streaming mode (and possibly creates 
 583      * a child encoder). */ 
 584     if (SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0) != SECSuccess
) { 
 585         result 
= PORT_GetError(); 
 596  * SecCmsEncoderUpdate - take content data delivery from the user 
 598  * "p7ecx" - encoder context 
 599  * "data" - content data 
 600  * "len" - length of content data 
 602  * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), 
 603  * then hand the data to the work_data fn 
 606 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx
, const void *data
, CFIndex len
) 
 609     SecCmsContentInfoRef cinfo
; 
 615     /* hand data to the innermost decoder */ 
 616     if (p7ecx
->childp7ecx
) { 
 618         result 
= SecCmsEncoderUpdate(p7ecx
->childp7ecx
, data
, len
); 
 620         /* we are at innermost decoder */ 
 621         /* find out about our inner content type - must be data */ 
 622         cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 623         childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 624         if (childtype 
!= SEC_OID_PKCS7_DATA
) 
 625             return errSecParam
; /* @@@ Maybe come up with a better error? */ 
 626         /* and we must not have preset data */ 
 627         if (cinfo
->content
.data 
!= NULL
) 
 628             return errSecParam
; /* @@@ Maybe come up with a better error? */ 
 630         /*  hand it the data so it can encode it (let DER trickle up the chain) */ 
 631         result 
= nss_cms_encoder_work_data(p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_TRUE
); 
 633             result 
= PORT_GetError(); 
 639  * SecCmsEncoderDestroy - stop all encoding 
 641  * we need to walk down the chain of encoders and the finish them from the innermost out 
 644 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx
) 
 646     /* XXX do this right! */ 
 649      * Finish any inner decoders before us so that all the encoded data is flushed 
 650      * This basically finishes all the decoders from the innermost to the outermost. 
 651      * Finishing an inner decoder may result in data being updated to the outer decoder 
 652      * while we are already in SecCmsEncoderFinish, but that's allright. 
 654     if (p7ecx
->childp7ecx
) 
 655         SecCmsEncoderDestroy(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */ 
 658      * On the way back up, there will be no more data (if we had an 
 659      * inner encoder, it is done now!) 
 660      * Flush out any remaining data and/or finish digests. 
 662     if (nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx 
== NULL
))) 
 665     p7ecx
->childp7ecx 
= NULL
; 
 667     /* kick the encoder back into working mode again. 
 668      * We turn off streaming stuff (which will cause the encoder to continue 
 669      * encoding happily, now that we have all the data (like digests) ready for it). 
 671     SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
); 
 672     SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
); 
 674     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 
 675     SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0); 
 678     SEC_ASN1EncoderFinish(p7ecx
->ecx
); 
 683  * SecCmsEncoderFinish - signal the end of data 
 685  * we need to walk down the chain of encoders and the finish them from the innermost out 
 688 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx
) 
 691     SecCmsContentInfoRef cinfo
; 
 695      * Finish any inner decoders before us so that all the encoded data is flushed 
 696      * This basically finishes all the decoders from the innermost to the outermost. 
 697      * Finishing an inner decoder may result in data being updated to the outer decoder 
 698      * while we are already in SecCmsEncoderFinish, but that's allright. 
 700     if (p7ecx
->childp7ecx
) { 
 701         result 
= SecCmsEncoderFinish(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */ 
 707      * On the way back up, there will be no more data (if we had an 
 708      * inner encoder, it is done now!) 
 709      * Flush out any remaining data and/or finish digests. 
 711     result 
= nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx 
== NULL
)); 
 713         result 
= PORT_GetError(); 
 717     p7ecx
->childp7ecx 
= NULL
; 
 719     /* find out about our inner content type - must be data */ 
 720     cinfo 
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
); 
 721     childtype 
= SecCmsContentInfoGetContentTypeTag(cinfo
); 
 722     if (childtype 
== SEC_OID_PKCS7_DATA 
&& cinfo
->content
.data 
== NULL
) { 
 723         SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
); 
 724         /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 
 725         result 
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0); 
 727             result 
= PORT_GetError(); 
 730     SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
); 
 732     if (p7ecx
->error 
&& !result
) 
 733         result 
= p7ecx
->error
; 
 736     SEC_ASN1EncoderFinish(p7ecx
->ecx
); 
 742 SecCmsMessageEncode(SecCmsMessageRef cmsg
, const SecAsn1Item 
*input
, 
 743                     CFMutableDataRef outBer
) 
 745     SecCmsEncoderRef encoder 
= NULL
; 
 748     if (!cmsg 
|| !outBer
) { 
 749         result 
= errSecParam
; 
 753     result 
= SecCmsEncoderCreate(cmsg
, 0, 0, outBer
, 0, 0, 0, 0, &encoder
); 
 758         result 
= SecCmsEncoderUpdate(encoder
, input
->Data
, input
->Length
); 
 760             SecCmsEncoderDestroy(encoder
); 
 764     result 
= SecCmsEncoderFinish(encoder
);