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
);