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();
181 PORT_SetError(0); // Clean the thread error since we've returned the error
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 PORT_SetError(0); // Clean the thread error since we've returned the error
197 SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */
204 * nss_cms_before_data - setup the current encoder to receive data
207 nss_cms_before_data(SecCmsEncoderRef p7ecx
)
211 SecCmsContentInfoRef cinfo
;
212 SecCmsEncoderRef childp7ecx
;
213 const SecAsn1Template
*template;
215 /* call _Encode_BeforeData handlers */
216 switch (p7ecx
->type
) {
217 case SEC_OID_PKCS7_SIGNED_DATA
:
218 /* we're encoding a signedData, so set up the digests */
219 rv
= SecCmsSignedDataEncodeBeforeData(p7ecx
->content
.signedData
);
221 case SEC_OID_PKCS7_DIGESTED_DATA
:
222 /* we're encoding a digestedData, so set up the digest */
223 rv
= SecCmsDigestedDataEncodeBeforeData(p7ecx
->content
.digestedData
);
225 case SEC_OID_PKCS7_ENVELOPED_DATA
:
226 rv
= SecCmsEnvelopedDataEncodeBeforeData(p7ecx
->content
.envelopedData
);
228 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
229 rv
= SecCmsEncryptedDataEncodeBeforeData(p7ecx
->content
.encryptedData
);
234 if (rv
!= SECSuccess
)
237 /* ok, now we have a pointer to cinfo */
238 /* find out what kind of data is encapsulated */
240 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
241 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
244 case SEC_OID_PKCS7_SIGNED_DATA
:
245 case SEC_OID_PKCS7_ENVELOPED_DATA
:
246 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
247 case SEC_OID_PKCS7_DIGESTED_DATA
:
249 case SEC_OID_PKCS7_DATA
: /* XXX here also??? maybe yes! */
251 /* in these cases, we need to set up a child encoder! */
252 /* create new encoder context */
253 childp7ecx
= PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
));
254 if (childp7ecx
== NULL
)
257 /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
258 * (which will encrypt and/or digest it)
259 * this needs to route back into our update function
260 * which finds the lowest encoding context & encrypts and computes digests */
261 childp7ecx
->type
= childtype
;
262 childp7ecx
->content
= cinfo
->content
;
263 /* use the non-recursive update function here, of course */
264 childp7ecx
->output
.outputfn
= nss_cms_encoder_update
;
265 childp7ecx
->output
.outputarg
= p7ecx
;
266 childp7ecx
->output
.berData
= NULL
;
267 childp7ecx
->cmsg
= p7ecx
->cmsg
;
269 template = SecCmsUtilGetTemplateByTypeTag(childtype
);
270 if (template == NULL
)
271 goto loser
; /* cannot happen */
273 /* now initialize the data for encoding the first third */
274 switch (childp7ecx
->type
) {
275 case SEC_OID_PKCS7_SIGNED_DATA
:
276 rv
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
);
278 case SEC_OID_PKCS7_ENVELOPED_DATA
:
279 rv
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
);
281 case SEC_OID_PKCS7_DIGESTED_DATA
:
282 rv
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
);
284 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
285 rv
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
);
287 case SEC_OID_PKCS7_DATA
:
294 if (rv
!= SECSuccess
)
298 * Initialize the BER encoder.
300 childp7ecx
->ecx
= SEC_ASN1EncoderStart(cinfo
->content
.pointer
, template,
301 nss_cms_encoder_out
, &(childp7ecx
->output
));
302 if (childp7ecx
->ecx
== NULL
)
305 childp7ecx
->ecxupdated
= PR_FALSE
;
308 * Indicate that we are streaming. We will be streaming until we
309 * get past the contents bytes.
311 SEC_ASN1EncoderSetStreaming(childp7ecx
->ecx
);
314 * The notify function will watch for the contents field.
316 SEC_ASN1EncoderSetNotifyProc(childp7ecx
->ecx
, nss_cms_encoder_notify
, childp7ecx
);
318 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
319 /* encoding process - we'll do that from the update function instead */
320 /* otherwise we'd be encoding data from a call of the notify function of the */
321 /* parent encoder (which would not work) */
323 /* this will kick off the encoding process & encode everything up to the content bytes,
324 * at which point the notify function sets streaming mode (and possibly creates
325 * another child encoder). */
326 if (SEC_ASN1EncoderUpdate(childp7ecx
->ecx
, NULL
, 0) != SECSuccess
)
329 p7ecx
->childp7ecx
= childp7ecx
;
332 case SEC_OID_PKCS7_DATA
:
333 p7ecx
->childp7ecx
= NULL
;
336 /* we do not know this type */
337 p7ecx
->error
= SEC_ERROR_BAD_DER
;
346 SEC_ASN1EncoderFinish(childp7ecx
->ecx
);
347 PORT_Free(childp7ecx
);
353 nss_cms_after_data(SecCmsEncoderRef p7ecx
)
355 OSStatus rv
= SECFailure
;
357 switch (p7ecx
->type
) {
358 case SEC_OID_PKCS7_SIGNED_DATA
:
359 /* this will finish the digests and sign */
360 rv
= SecCmsSignedDataEncodeAfterData(p7ecx
->content
.signedData
);
362 case SEC_OID_PKCS7_ENVELOPED_DATA
:
363 rv
= SecCmsEnvelopedDataEncodeAfterData(p7ecx
->content
.envelopedData
);
365 case SEC_OID_PKCS7_DIGESTED_DATA
:
366 rv
= SecCmsDigestedDataEncodeAfterData(p7ecx
->content
.digestedData
);
368 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
369 rv
= SecCmsEncryptedDataEncodeAfterData(p7ecx
->content
.encryptedData
);
371 case SEC_OID_PKCS7_DATA
:
382 * nss_cms_encoder_work_data - process incoming data
384 * (from the user or the next encoding layer)
385 * Here, we need to digest and/or encrypt, then pass it on
389 nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, SecAsn1Item
* dest
,
390 const unsigned char *data
, size_t len
,
391 Boolean final
, Boolean innermost
)
393 unsigned char *buf
= NULL
;
395 SecCmsContentInfoRef cinfo
;
397 rv
= SECSuccess
; /* may as well be optimistic */
400 * We should really have data to process, or we should be trying
401 * to finish/flush the last block. (This is an overly paranoid
402 * check since all callers are in this file and simple inspection
403 * proves they do it right. But it could find a bug in future
404 * modifications/development, that is why it is here.)
406 PORT_Assert ((data
!= NULL
&& len
) || final
);
407 PORT_Assert (len
< UINT_MAX
); /* overflow check for later cast */
409 /* we got data (either from the caller, or from a lower level encoder) */
410 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
412 /* Update the running digest. */
413 if (len
&& cinfo
->digcx
!= NULL
)
414 SecCmsDigestContextUpdate(cinfo
->digcx
, data
, len
);
416 /* Encrypt this chunk. */
417 if (cinfo
->ciphcx
!= NULL
) {
418 unsigned int inlen
; /* length of data being encrypted */
419 unsigned int outlen
= 0; /* length of encrypted data */
420 unsigned int buflen
; /* length available for encrypted data */
422 /* 64 bits cast: only an issue if unsigned int is smaller than size_t.
423 Worst case is you will truncate a CMS blob bigger than 4GB when
425 inlen
= (unsigned int)len
;
427 buflen
= SecCmsCipherContextEncryptLength(cinfo
->ciphcx
, inlen
, final
);
430 * No output is expected, but the input data may be buffered
431 * so we still have to call Encrypt.
433 rv
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, NULL
, NULL
, 0,
443 buf
= (unsigned char*)PORT_ArenaAlloc(p7ecx
->cmsg
->poolp
, buflen
);
445 buf
= (unsigned char*)PORT_Alloc(buflen
);
450 rv
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, buf
, &outlen
, buflen
,
455 if (rv
!= SECSuccess
)
456 /* encryption or malloc failed? */
462 * at this point (data,len) has everything we'd like to give to the CURRENT encoder
463 * (which will encode it, then hand it back to the user or the parent encoder)
464 * We don't encode the data if we're innermost and we're told not to include the data
466 if (p7ecx
->ecx
!= NULL
&& len
&& (!innermost
|| cinfo
->rawContent
!= NULL
))
467 rv
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, (const char *)data
, len
);
471 if (cinfo
->ciphcx
!= NULL
) {
475 } else if (buf
!= NULL
) {
483 * nss_cms_encoder_update - deliver encoded data to the next higher level
485 * no recursion here because we REALLY want to end up at the next higher encoder!
488 nss_cms_encoder_update(void *arg
, const char *data
, size_t len
)
490 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
491 SecCmsEncoderRef p7ecx
= (SecCmsEncoderRef
)arg
;
493 (void)nss_cms_encoder_work_data (p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_FALSE
);
497 * SecCmsEncoderCreate - set up encoding of a CMS message
499 * "cmsg" - message to encode
500 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
501 * will not be called if NULL.
502 * "dest" - if non-NULL, pointer to SecAsn1Item that will hold the DER-encoded output
503 * "destpoolp" - pool to allocate DER-encoded output in
504 * "pwfn", pwfn_arg" - callback function for getting token password
505 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
506 * "detached_digestalgs", "detached_digests" - digests from detached content
509 SecCmsEncoderCreate(SecCmsMessageRef cmsg
,
510 SecCmsContentCallback outputfn
, void *outputarg
,
511 CFMutableDataRef outBer
,
512 PK11PasswordFunc pwfn
, void *pwfn_arg
,
513 SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
,
514 SecCmsEncoderRef
*outEncoder
)
516 SecCmsEncoderRef p7ecx
;
518 SecCmsContentInfoRef cinfo
;
520 /* Clear the thread error to clean up dirty threads */
523 SecCmsMessageSetEncodingParams(cmsg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
);
525 p7ecx
= (SecCmsEncoderRef
)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
));
527 result
= errSecAllocate
;
532 p7ecx
->output
.outputfn
= outputfn
;
533 p7ecx
->output
.outputarg
= outputarg
;
534 p7ecx
->output
.berData
= outBer
;
536 p7ecx
->type
= SEC_OID_UNKNOWN
;
538 cinfo
= SecCmsMessageGetContentInfo(cmsg
);
540 switch (SecCmsContentInfoGetContentTypeTag(cinfo
)) {
541 case SEC_OID_PKCS7_SIGNED_DATA
:
542 result
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
);
544 case SEC_OID_PKCS7_ENVELOPED_DATA
:
545 result
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
);
547 case SEC_OID_PKCS7_DIGESTED_DATA
:
548 result
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
);
550 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
551 result
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
);
554 /* @@@ We need a better error for unsupported message types. */
555 result
= errSecParam
;
564 /* Initialize the BER encoder.
565 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
566 p7ecx
->ecx
= SEC_ASN1EncoderStart(cmsg
, SecCmsMessageTemplate
,
567 nss_cms_encoder_out
, &(p7ecx
->output
));
568 if (p7ecx
->ecx
== NULL
) {
569 result
= PORT_GetError();
571 PORT_SetError(0); // Clean the thread error since we've returned the error
574 p7ecx
->ecxupdated
= PR_FALSE
;
577 * Indicate that we are streaming. We will be streaming until we
578 * get past the contents bytes.
580 SEC_ASN1EncoderSetStreaming(p7ecx
->ecx
);
583 * The notify function will watch for the contents field.
585 SEC_ASN1EncoderSetNotifyProc(p7ecx
->ecx
, nss_cms_encoder_notify
, p7ecx
);
587 /* this will kick off the encoding process & encode everything up to the content bytes,
588 * at which point the notify function sets streaming mode (and possibly creates
589 * a child encoder). */
590 if (SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0) != SECSuccess
) {
591 result
= PORT_GetError();
593 PORT_SetError(0); // Clean the thread error since we've returned the error
603 * SecCmsEncoderUpdate - take content data delivery from the user
605 * "p7ecx" - encoder context
606 * "data" - content data
607 * "len" - length of content data
609 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
610 * then hand the data to the work_data fn
613 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx
, const void *data
, CFIndex len
)
616 SecCmsContentInfoRef cinfo
;
626 /* hand data to the innermost decoder */
627 if (p7ecx
->childp7ecx
) {
629 result
= SecCmsEncoderUpdate(p7ecx
->childp7ecx
, data
, len
);
631 /* we are at innermost decoder */
632 /* find out about our inner content type - must be data */
633 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
634 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
635 if (childtype
!= SEC_OID_PKCS7_DATA
)
636 return errSecParam
; /* @@@ Maybe come up with a better error? */
637 /* and we must not have preset data */
638 if (cinfo
->content
.data
!= NULL
)
639 return errSecParam
; /* @@@ Maybe come up with a better error? */
641 /* hand it the data so it can encode it (let DER trickle up the chain) */
642 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_TRUE
);
644 result
= PORT_GetError();
645 PORT_SetError(0); // Clean the thread error since we've returned the error
652 * SecCmsEncoderDestroy - stop all encoding
654 * we need to walk down the chain of encoders and the finish them from the innermost out
657 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx
)
659 /* XXX do this right! */
662 * Finish any inner decoders before us so that all the encoded data is flushed
663 * This basically finishes all the decoders from the innermost to the outermost.
664 * Finishing an inner decoder may result in data being updated to the outer decoder
665 * while we are already in SecCmsEncoderFinish, but that's allright.
667 if (p7ecx
->childp7ecx
)
668 SecCmsEncoderDestroy(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
671 * On the way back up, there will be no more data (if we had an
672 * inner encoder, it is done now!)
673 * Flush out any remaining data and/or finish digests.
675 if (nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
)))
678 p7ecx
->childp7ecx
= NULL
;
680 /* kick the encoder back into working mode again.
681 * We turn off streaming stuff (which will cause the encoder to continue
682 * encoding happily, now that we have all the data (like digests) ready for it).
684 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
685 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
687 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
688 SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
691 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
696 * SecCmsEncoderFinish - signal the end of data
698 * we need to walk down the chain of encoders and the finish them from the innermost out
701 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx
)
704 SecCmsContentInfoRef cinfo
;
708 * Finish any inner decoders before us so that all the encoded data is flushed
709 * This basically finishes all the decoders from the innermost to the outermost.
710 * Finishing an inner decoder may result in data being updated to the outer decoder
711 * while we are already in SecCmsEncoderFinish, but that's allright.
713 if (p7ecx
->childp7ecx
) {
714 result
= SecCmsEncoderFinish(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
720 * On the way back up, there will be no more data (if we had an
721 * inner encoder, it is done now!)
722 * Flush out any remaining data and/or finish digests.
724 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
));
726 result
= PORT_GetError();
730 p7ecx
->childp7ecx
= NULL
;
732 /* find out about our inner content type - must be data */
733 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
734 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
735 if (childtype
== SEC_OID_PKCS7_DATA
&& cinfo
->content
.data
== NULL
) {
736 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
737 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
738 result
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
740 result
= PORT_GetError();
743 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
745 if (p7ecx
->error
&& !result
)
746 result
= p7ecx
->error
;
749 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
751 PORT_SetError(0); // Clean the thread error since we've returned the error
756 SecCmsMessageEncode(SecCmsMessageRef cmsg
, const SecAsn1Item
*input
,
757 CFMutableDataRef outBer
)
759 SecCmsEncoderRef encoder
= NULL
;
762 if (!cmsg
|| !outBer
) {
763 result
= errSecParam
;
767 result
= SecCmsEncoderCreate(cmsg
, 0, 0, outBer
, 0, 0, 0, 0, &encoder
);
772 result
= SecCmsEncoderUpdate(encoder
, input
->Data
, input
->Length
);
774 SecCmsEncoderDestroy(encoder
);
778 result
= SecCmsEncoderFinish(encoder
);