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
;
557 /* Initialize the BER encoder.
558 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
559 p7ecx
->ecx
= SEC_ASN1EncoderStart(cmsg
, SecCmsMessageTemplate
,
560 nss_cms_encoder_out
, &(p7ecx
->output
));
561 if (p7ecx
->ecx
== NULL
) {
562 result
= PORT_GetError();
566 p7ecx
->ecxupdated
= PR_FALSE
;
569 * Indicate that we are streaming. We will be streaming until we
570 * get past the contents bytes.
572 SEC_ASN1EncoderSetStreaming(p7ecx
->ecx
);
575 * The notify function will watch for the contents field.
577 SEC_ASN1EncoderSetNotifyProc(p7ecx
->ecx
, nss_cms_encoder_notify
, p7ecx
);
579 /* this will kick off the encoding process & encode everything up to the content bytes,
580 * at which point the notify function sets streaming mode (and possibly creates
581 * a child encoder). */
582 if (SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0) != SECSuccess
) {
583 result
= PORT_GetError();
594 * SecCmsEncoderUpdate - take content data delivery from the user
596 * "p7ecx" - encoder context
597 * "data" - content data
598 * "len" - length of content data
600 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
601 * then hand the data to the work_data fn
604 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx
, const void *data
, CFIndex len
)
607 SecCmsContentInfoRef cinfo
;
617 /* hand data to the innermost decoder */
618 if (p7ecx
->childp7ecx
) {
620 result
= SecCmsEncoderUpdate(p7ecx
->childp7ecx
, data
, len
);
622 /* we are at innermost decoder */
623 /* find out about our inner content type - must be data */
624 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
625 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
626 if (childtype
!= SEC_OID_PKCS7_DATA
)
627 return errSecParam
; /* @@@ Maybe come up with a better error? */
628 /* and we must not have preset data */
629 if (cinfo
->content
.data
!= NULL
)
630 return errSecParam
; /* @@@ Maybe come up with a better error? */
632 /* hand it the data so it can encode it (let DER trickle up the chain) */
633 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_TRUE
);
635 result
= PORT_GetError();
641 * SecCmsEncoderDestroy - stop all encoding
643 * we need to walk down the chain of encoders and the finish them from the innermost out
646 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx
)
648 /* XXX do this right! */
651 * Finish any inner decoders before us so that all the encoded data is flushed
652 * This basically finishes all the decoders from the innermost to the outermost.
653 * Finishing an inner decoder may result in data being updated to the outer decoder
654 * while we are already in SecCmsEncoderFinish, but that's allright.
656 if (p7ecx
->childp7ecx
)
657 SecCmsEncoderDestroy(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
660 * On the way back up, there will be no more data (if we had an
661 * inner encoder, it is done now!)
662 * Flush out any remaining data and/or finish digests.
664 if (nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
)))
667 p7ecx
->childp7ecx
= NULL
;
669 /* kick the encoder back into working mode again.
670 * We turn off streaming stuff (which will cause the encoder to continue
671 * encoding happily, now that we have all the data (like digests) ready for it).
673 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
674 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
676 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
677 SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
680 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
685 * SecCmsEncoderFinish - signal the end of data
687 * we need to walk down the chain of encoders and the finish them from the innermost out
690 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx
)
693 SecCmsContentInfoRef cinfo
;
697 * Finish any inner decoders before us so that all the encoded data is flushed
698 * This basically finishes all the decoders from the innermost to the outermost.
699 * Finishing an inner decoder may result in data being updated to the outer decoder
700 * while we are already in SecCmsEncoderFinish, but that's allright.
702 if (p7ecx
->childp7ecx
) {
703 result
= SecCmsEncoderFinish(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
709 * On the way back up, there will be no more data (if we had an
710 * inner encoder, it is done now!)
711 * Flush out any remaining data and/or finish digests.
713 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
));
715 result
= PORT_GetError();
719 p7ecx
->childp7ecx
= NULL
;
721 /* find out about our inner content type - must be data */
722 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
723 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
724 if (childtype
== SEC_OID_PKCS7_DATA
&& cinfo
->content
.data
== NULL
) {
725 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
726 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
727 result
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
729 result
= PORT_GetError();
732 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
734 if (p7ecx
->error
&& !result
)
735 result
= p7ecx
->error
;
738 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
744 SecCmsMessageEncode(SecCmsMessageRef cmsg
, const SecAsn1Item
*input
,
745 CFMutableDataRef outBer
)
747 SecCmsEncoderRef encoder
= NULL
;
750 if (!cmsg
|| !outBer
) {
751 result
= errSecParam
;
755 result
= SecCmsEncoderCreate(cmsg
, 0, 0, outBer
, 0, 0, 0, 0, &encoder
);
760 result
= SecCmsEncoderUpdate(encoder
, input
->Data
, input
->Length
);
762 SecCmsEncoderDestroy(encoder
);
766 result
= SecCmsEncoderFinish(encoder
);