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>
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
52 struct nss_cms_encoder_output
{
53 SecCmsContentCallback outputfn
;
55 PLArenaPool
*destpoolp
;
59 struct SecCmsEncoderStr
{
60 SEC_ASN1EncoderContext
* ecx
; /* ASN.1 encoder context */
61 Boolean ecxupdated
; /* true if data was handed in */
62 SecCmsMessageRef cmsg
; /* pointer to the root message */
63 SECOidTag type
; /* type tag of the current content */
64 SecCmsContent content
; /* pointer to current content */
65 struct nss_cms_encoder_output output
; /* output function */
66 int error
; /* error code */
67 SecCmsEncoderRef childp7ecx
; /* link to child encoder context */
70 static OSStatus
nss_cms_before_data(SecCmsEncoderRef p7ecx
);
71 static OSStatus
nss_cms_after_data(SecCmsEncoderRef p7ecx
);
72 static OSStatus
nss_cms_encoder_update(SecCmsEncoderRef p7ecx
, const char *data
, size_t len
);
73 static OSStatus
nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, CSSM_DATA_PTR dest
,
74 const unsigned char *data
, size_t len
,
75 Boolean final
, Boolean innermost
);
77 extern const SecAsn1Template SecCmsMessageTemplate
[];
80 * The little output function that the ASN.1 encoder calls to hand
81 * us bytes which we in turn hand back to our caller (via the callback
85 nss_cms_encoder_out(void *arg
, const char *buf
, size_t len
,
86 int depth
, SEC_ASN1EncodingPart data_kind
)
88 struct nss_cms_encoder_output
*output
= (struct nss_cms_encoder_output
*)arg
;
95 fprintf(stderr
, "kind = %d, depth = %d, len = %lu\n", data_kind
, depth
, len
);
96 for (i
=0; i
< len
; i
++) {
97 fprintf(stderr
, " %02x%s", (unsigned int)buf
[i
] & 0xff, ((i
% 16) == 15) ? "\n" : "");
100 fprintf(stderr
, "\n");
103 if (output
->outputfn
!= NULL
)
104 /* call output callback with DER data */
105 output
->outputfn(output
->outputarg
, buf
, len
);
107 if (output
->dest
!= NULL
) {
108 /* store DER data in CSSM_DATA */
109 offset
= output
->dest
->Length
;
111 dest
= (unsigned char *)PORT_ArenaAlloc(output
->destpoolp
, len
);
113 dest
= (unsigned char *)PORT_ArenaGrow(output
->destpoolp
,
115 output
->dest
->Length
,
116 output
->dest
->Length
+ len
);
122 output
->dest
->Data
= dest
;
123 output
->dest
->Length
+= len
;
126 PORT_Memcpy(output
->dest
->Data
+ offset
, buf
, len
);
131 * nss_cms_encoder_notify - ASN.1 encoder callback
133 * this function is called by the ASN.1 encoder before and after the encoding of
134 * every object. here, it is used to keep track of data structures, set up
135 * encryption and/or digesting and possibly set up child encoders.
138 nss_cms_encoder_notify(void *arg
, Boolean before
, void *dest
, int depth
)
140 SecCmsEncoderRef p7ecx
;
141 SecCmsContentInfoRef rootcinfo
, cinfo
;
142 Boolean after
= !before
;
146 p7ecx
= (SecCmsEncoderRef
)arg
;
147 PORT_Assert(p7ecx
!= NULL
);
149 rootcinfo
= &(p7ecx
->cmsg
->contentInfo
);
152 fprintf(stderr
, "%6.6s, dest = %p, depth = %d\n", before
? "before" : "after", dest
, depth
);
156 * Watch for the content field, at which point we want to instruct
157 * the ASN.1 encoder to start taking bytes from the buffer.
159 switch (p7ecx
->type
) {
161 case SEC_OID_UNKNOWN
:
162 /* we're still in the root message */
163 if (after
&& dest
== &(rootcinfo
->contentType
)) {
164 /* got the content type OID now - so find out the type tag */
165 p7ecx
->type
= SecCmsContentInfoGetContentTypeTag(rootcinfo
);
166 /* set up a pointer to our current content */
167 p7ecx
->content
= rootcinfo
->content
;
171 case SEC_OID_PKCS7_DATA
:
173 if (before
&& dest
== &(rootcinfo
->rawContent
)) {
174 /* just set up encoder to grab from user - no encryption or digesting */
175 if ((item
= rootcinfo
->content
.data
) != NULL
)
176 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
);
178 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
);
179 SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */
183 case SEC_OID_PKCS7_SIGNED_DATA
:
184 case SEC_OID_PKCS7_ENVELOPED_DATA
:
185 case SEC_OID_PKCS7_DIGESTED_DATA
:
186 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
188 /* when we know what the content is, we encode happily until we reach the inner content */
189 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
190 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
192 if (after
&& dest
== &(cinfo
->contentType
)) {
193 /* we're right before encoding the data (if we have some or not) */
194 /* (for encrypted data, we're right before the contentEncAlg which may change */
195 /* in nss_cms_before_data because of IV calculation when setting up encryption) */
196 if (nss_cms_before_data(p7ecx
) != SECSuccess
) {
197 p7ecx
->error
= PORT_GetError();
198 PORT_SetError(0); // Clean the thread error since we've returned the error
201 if (before
&& dest
== &(cinfo
->rawContent
)) {
202 if ( ((childtype
== SEC_OID_PKCS7_DATA
) || (childtype
== SEC_OID_OTHER
)) &&
203 ((item
= cinfo
->content
.data
) != NULL
))
204 /* we have data - feed it in */
205 (void)nss_cms_encoder_work_data(p7ecx
, NULL
, item
->Data
, item
->Length
, PR_TRUE
, PR_TRUE
);
207 /* else try to get it from user */
208 SEC_ASN1EncoderSetTakeFromBuf(p7ecx
->ecx
);
210 if (after
&& dest
== &(cinfo
->rawContent
)) {
211 if (nss_cms_after_data(p7ecx
) != SECSuccess
) {
212 p7ecx
->error
= PORT_GetError();
213 PORT_SetError(0); // Clean the thread error since we've returned the error
215 SEC_ASN1EncoderClearNotifyProc(p7ecx
->ecx
); /* no need to get notified anymore */
222 * nss_cms_before_data - setup the current encoder to receive data
225 nss_cms_before_data(SecCmsEncoderRef p7ecx
)
229 SecCmsContentInfoRef cinfo
;
230 SecCmsEncoderRef childp7ecx
;
231 const SecAsn1Template
*template;
233 /* call _Encode_BeforeData handlers */
234 switch (p7ecx
->type
) {
235 case SEC_OID_PKCS7_SIGNED_DATA
:
236 /* we're encoding a signedData, so set up the digests */
237 rv
= SecCmsSignedDataEncodeBeforeData(p7ecx
->content
.signedData
);
239 case SEC_OID_PKCS7_DIGESTED_DATA
:
240 /* we're encoding a digestedData, so set up the digest */
241 rv
= SecCmsDigestedDataEncodeBeforeData(p7ecx
->content
.digestedData
);
243 case SEC_OID_PKCS7_ENVELOPED_DATA
:
244 rv
= SecCmsEnvelopedDataEncodeBeforeData(p7ecx
->content
.envelopedData
);
246 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
247 rv
= SecCmsEncryptedDataEncodeBeforeData(p7ecx
->content
.encryptedData
);
252 if (rv
!= SECSuccess
)
255 /* ok, now we have a pointer to cinfo */
256 /* find out what kind of data is encapsulated */
258 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
259 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
262 case SEC_OID_PKCS7_SIGNED_DATA
:
263 case SEC_OID_PKCS7_ENVELOPED_DATA
:
264 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
265 case SEC_OID_PKCS7_DIGESTED_DATA
:
267 case SEC_OID_PKCS7_DATA
: /* XXX here also??? maybe yes! */
269 /* in these cases, we need to set up a child encoder! */
270 /* create new encoder context */
271 childp7ecx
= PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
));
272 if (childp7ecx
== NULL
)
275 /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
276 * (which will encrypt and/or digest it)
277 * this needs to route back into our update function
278 * which finds the lowest encoding context & encrypts and computes digests */
279 childp7ecx
->type
= childtype
;
280 childp7ecx
->content
= cinfo
->content
;
281 /* use the non-recursive update function here, of course */
282 childp7ecx
->output
.outputfn
= (SecCmsContentCallback
)nss_cms_encoder_update
;
283 childp7ecx
->output
.outputarg
= p7ecx
;
284 childp7ecx
->output
.destpoolp
= NULL
;
285 childp7ecx
->output
.dest
= NULL
;
286 childp7ecx
->cmsg
= p7ecx
->cmsg
;
288 template = SecCmsUtilGetTemplateByTypeTag(childtype
);
289 if (template == NULL
)
290 goto loser
; /* cannot happen */
292 /* now initialize the data for encoding the first third */
293 switch (childp7ecx
->type
) {
294 case SEC_OID_PKCS7_SIGNED_DATA
:
295 rv
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
);
297 case SEC_OID_PKCS7_ENVELOPED_DATA
:
298 rv
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
);
300 case SEC_OID_PKCS7_DIGESTED_DATA
:
301 rv
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
);
303 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
304 rv
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
);
306 case SEC_OID_PKCS7_DATA
:
314 if (rv
!= SECSuccess
)
318 * Initialize the BER encoder.
320 childp7ecx
->ecx
= SEC_ASN1EncoderStart(cinfo
->content
.pointer
, template,
321 nss_cms_encoder_out
, &(childp7ecx
->output
));
322 if (childp7ecx
->ecx
== NULL
)
325 childp7ecx
->ecxupdated
= PR_FALSE
;
328 * Indicate that we are streaming. We will be streaming until we
329 * get past the contents bytes.
331 SEC_ASN1EncoderSetStreaming(childp7ecx
->ecx
);
334 * The notify function will watch for the contents field.
336 SEC_ASN1EncoderSetNotifyProc(childp7ecx
->ecx
, nss_cms_encoder_notify
, childp7ecx
);
338 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
339 /* encoding process - we'll do that from the update function instead */
340 /* otherwise we'd be encoding data from a call of the notify function of the */
341 /* parent encoder (which would not work) */
343 /* this will kick off the encoding process & encode everything up to the content bytes,
344 * at which point the notify function sets streaming mode (and possibly creates
345 * another child encoder). */
346 if (SEC_ASN1EncoderUpdate(childp7ecx
->ecx
, NULL
, 0) != SECSuccess
)
349 p7ecx
->childp7ecx
= childp7ecx
;
352 case SEC_OID_PKCS7_DATA
:
354 p7ecx
->childp7ecx
= NULL
;
357 /* we do not know this type */
358 p7ecx
->error
= SEC_ERROR_BAD_DER
;
367 SEC_ASN1EncoderFinish(childp7ecx
->ecx
);
368 PORT_Free(childp7ecx
);
374 nss_cms_after_data(SecCmsEncoderRef p7ecx
)
376 OSStatus rv
= SECFailure
;
378 switch (p7ecx
->type
) {
379 case SEC_OID_PKCS7_SIGNED_DATA
:
380 /* this will finish the digests and sign */
381 rv
= SecCmsSignedDataEncodeAfterData(p7ecx
->content
.signedData
);
383 case SEC_OID_PKCS7_ENVELOPED_DATA
:
384 rv
= SecCmsEnvelopedDataEncodeAfterData(p7ecx
->content
.envelopedData
);
386 case SEC_OID_PKCS7_DIGESTED_DATA
:
387 rv
= SecCmsDigestedDataEncodeAfterData(p7ecx
->content
.digestedData
);
389 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
390 rv
= SecCmsEncryptedDataEncodeAfterData(p7ecx
->content
.encryptedData
);
392 case SEC_OID_PKCS7_DATA
:
404 * nss_cms_encoder_work_data - process incoming data
406 * (from the user or the next encoding layer)
407 * Here, we need to digest and/or encrypt, then pass it on
410 nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx
, CSSM_DATA_PTR dest
,
411 const unsigned char *data
, size_t len
,
412 Boolean final
, Boolean innermost
)
414 unsigned char *buf
= NULL
;
416 SecCmsContentInfoRef cinfo
;
418 rv
= SECSuccess
; /* may as well be optimistic */
421 * We should really have data to process, or we should be trying
422 * to finish/flush the last block. (This is an overly paranoid
423 * check since all callers are in this file and simple inspection
424 * proves they do it right. But it could find a bug in future
425 * modifications/development, that is why it is here.)
427 PORT_Assert ((data
!= NULL
&& len
) || final
);
429 /* we got data (either from the caller, or from a lower level encoder) */
430 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
432 /* Update the running digest. */
433 if (len
&& cinfo
->digcx
!= NULL
)
434 SecCmsDigestContextUpdate(cinfo
->digcx
, data
, len
);
436 /* Encrypt this chunk. */
437 if (cinfo
->ciphcx
!= NULL
) {
438 CSSM_SIZE inlen
; /* length of data being encrypted */
439 CSSM_SIZE outlen
= 0; /* length of encrypted data */
440 CSSM_SIZE buflen
; /* length available for encrypted data */
443 buflen
= SecCmsCipherContextEncryptLength(cinfo
->ciphcx
, inlen
, final
);
446 * No output is expected, but the input data may be buffered
447 * so we still have to call Encrypt.
449 rv
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, NULL
, NULL
, 0,
459 buf
= (unsigned char*)PORT_ArenaAlloc(p7ecx
->cmsg
->poolp
, buflen
);
461 buf
= (unsigned char*)PORT_Alloc(buflen
);
466 rv
= SecCmsCipherContextEncrypt(cinfo
->ciphcx
, buf
, &outlen
, buflen
,
471 if (rv
!= SECSuccess
)
472 /* encryption or malloc failed? */
478 * at this point (data,len) has everything we'd like to give to the CURRENT encoder
479 * (which will encode it, then hand it back to the user or the parent encoder)
480 * We don't encode the data if we're innermost and we're told not to include the data
482 if (p7ecx
->ecx
!= NULL
&& len
&& (!innermost
|| cinfo
->rawContent
!= NULL
))
483 rv
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, (const char *)data
, len
);
487 if (cinfo
->ciphcx
!= NULL
) {
491 } else if (buf
!= NULL
) {
499 * nss_cms_encoder_update - deliver encoded data to the next higher level
501 * no recursion here because we REALLY want to end up at the next higher encoder!
504 nss_cms_encoder_update(SecCmsEncoderRef p7ecx
, const char *data
, size_t len
)
506 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
507 return nss_cms_encoder_work_data (p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_FALSE
);
511 * SecCmsEncoderCreate - set up encoding of a CMS message
513 * "cmsg" - message to encode
514 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
515 * will not be called if NULL.
516 * "dest" - if non-NULL, pointer to CSSM_DATA that will hold the DER-encoded output
517 * "destpoolp" - pool to allocate DER-encoded output in
518 * "pwfn", pwfn_arg" - callback function for getting token password
519 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
520 * "detached_digestalgs", "detached_digests" - digests from detached content
523 SecCmsEncoderCreate(SecCmsMessageRef cmsg
,
524 SecCmsContentCallback outputfn
, void *outputarg
,
525 CSSM_DATA_PTR dest
, SecArenaPoolRef destpool
,
526 PK11PasswordFunc pwfn
, void *pwfn_arg
,
527 SecCmsGetDecryptKeyCallback decrypt_key_cb
, void *decrypt_key_cb_arg
,
528 SECAlgorithmID
**detached_digestalgs
, CSSM_DATA_PTR
*detached_digests
,
529 SecCmsEncoderRef
*outEncoder
)
531 SecCmsEncoderRef p7ecx
;
533 SecCmsContentInfoRef cinfo
;
535 /* Clear the thread error to clean up dirty threads */
538 SecCmsMessageSetEncodingParams(cmsg
, pwfn
, pwfn_arg
, decrypt_key_cb
, decrypt_key_cb_arg
,
539 detached_digestalgs
, detached_digests
);
541 p7ecx
= (SecCmsEncoderRef
)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr
));
548 p7ecx
->output
.outputfn
= outputfn
;
549 p7ecx
->output
.outputarg
= outputarg
;
550 p7ecx
->output
.dest
= dest
;
551 p7ecx
->output
.destpoolp
= (PLArenaPool
*)destpool
;
552 p7ecx
->type
= SEC_OID_UNKNOWN
;
554 cinfo
= SecCmsMessageGetContentInfo(cmsg
);
556 switch (SecCmsContentInfoGetContentTypeTag(cinfo
)) {
557 case SEC_OID_PKCS7_SIGNED_DATA
:
558 result
= SecCmsSignedDataEncodeBeforeStart(cinfo
->content
.signedData
);
560 case SEC_OID_PKCS7_ENVELOPED_DATA
:
561 result
= SecCmsEnvelopedDataEncodeBeforeStart(cinfo
->content
.envelopedData
);
563 case SEC_OID_PKCS7_DIGESTED_DATA
:
564 result
= SecCmsDigestedDataEncodeBeforeStart(cinfo
->content
.digestedData
);
566 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
567 result
= SecCmsEncryptedDataEncodeBeforeStart(cinfo
->content
.encryptedData
);
570 /* @@@ We need a better error for unsupported message types. */
580 /* Initialize the BER encoder.
581 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
582 p7ecx
->ecx
= SEC_ASN1EncoderStart(cmsg
, SecCmsMessageTemplate
,
583 nss_cms_encoder_out
, &(p7ecx
->output
));
584 if (p7ecx
->ecx
== NULL
) {
585 result
= PORT_GetError();
587 PORT_SetError(0); // Clean the thread error since we've returned the error
590 p7ecx
->ecxupdated
= PR_FALSE
;
593 * Indicate that we are streaming. We will be streaming until we
594 * get past the contents bytes.
596 SEC_ASN1EncoderSetStreaming(p7ecx
->ecx
);
599 * The notify function will watch for the contents field.
601 SEC_ASN1EncoderSetNotifyProc(p7ecx
->ecx
, nss_cms_encoder_notify
, p7ecx
);
603 /* this will kick off the encoding process & encode everything up to the content bytes,
604 * at which point the notify function sets streaming mode (and possibly creates
605 * a child encoder). */
606 if (SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0) != SECSuccess
) {
607 result
= PORT_GetError();
609 PORT_SetError(0); // Clean the thread error since we've returned the error
619 * SecCmsEncoderUpdate - take content data delivery from the user
621 * "p7ecx" - encoder context
622 * "data" - content data
623 * "len" - length of content data
625 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
626 * then hand the data to the work_data fn
629 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx
, const void *data
, CFIndex len
)
632 SecCmsContentInfoRef cinfo
;
642 /* hand data to the innermost decoder */
643 if (p7ecx
->childp7ecx
) {
645 result
= SecCmsEncoderUpdate(p7ecx
->childp7ecx
, data
, len
);
647 /* we are at innermost decoder */
648 /* find out about our inner content type - must be data */
649 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
650 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
651 if ((childtype
!= SEC_OID_PKCS7_DATA
) && (childtype
!= SEC_OID_OTHER
))
652 return paramErr
; /* @@@ Maybe come up with a better error? */
653 /* and we must not have preset data */
654 if (cinfo
->content
.data
!= NULL
)
655 return paramErr
; /* @@@ Maybe come up with a better error? */
657 /* hand it the data so it can encode it (let DER trickle up the chain) */
658 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, (const unsigned char *)data
, len
, PR_FALSE
, PR_TRUE
);
660 result
= PORT_GetError();
661 PORT_SetError(0); // Clean the thread error since we've returned the error
668 * SecCmsEncoderDestroy - stop all encoding
670 * we need to walk down the chain of encoders and the finish them from the innermost out
673 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx
)
675 /* XXX do this right! */
678 * Finish any inner decoders before us so that all the encoded data is flushed
679 * This basically finishes all the decoders from the innermost to the outermost.
680 * Finishing an inner decoder may result in data being updated to the outer decoder
681 * while we are already in SecCmsEncoderFinish, but that's allright.
683 if (p7ecx
->childp7ecx
)
684 SecCmsEncoderDestroy(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
687 * On the way back up, there will be no more data (if we had an
688 * inner encoder, it is done now!)
689 * Flush out any remaining data and/or finish digests.
691 if (nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
)))
694 p7ecx
->childp7ecx
= NULL
;
696 /* kick the encoder back into working mode again.
697 * We turn off streaming stuff (which will cause the encoder to continue
698 * encoding happily, now that we have all the data (like digests) ready for it).
700 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
701 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
703 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
704 SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
707 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
712 * SecCmsEncoderFinish - signal the end of data
714 * we need to walk down the chain of encoders and the finish them from the innermost out
717 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx
)
720 SecCmsContentInfoRef cinfo
;
724 * Finish any inner decoders before us so that all the encoded data is flushed
725 * This basically finishes all the decoders from the innermost to the outermost.
726 * Finishing an inner decoder may result in data being updated to the outer decoder
727 * while we are already in SecCmsEncoderFinish, but that's allright.
729 if (p7ecx
->childp7ecx
) {
730 result
= SecCmsEncoderFinish(p7ecx
->childp7ecx
); /* frees p7ecx->childp7ecx */
736 * On the way back up, there will be no more data (if we had an
737 * inner encoder, it is done now!)
738 * Flush out any remaining data and/or finish digests.
740 result
= nss_cms_encoder_work_data(p7ecx
, NULL
, NULL
, 0, PR_TRUE
, (p7ecx
->childp7ecx
== NULL
));
742 result
= PORT_GetError();
746 p7ecx
->childp7ecx
= NULL
;
748 /* find out about our inner content type - must be data */
749 cinfo
= SecCmsContentGetContentInfo(p7ecx
->content
.pointer
, p7ecx
->type
);
750 childtype
= SecCmsContentInfoGetContentTypeTag(cinfo
);
751 if ( ((childtype
== SEC_OID_PKCS7_DATA
) || (childtype
== SEC_OID_OTHER
)) &&
752 (cinfo
->content
.data
== NULL
)) {
753 SEC_ASN1EncoderClearTakeFromBuf(p7ecx
->ecx
);
754 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
755 result
= SEC_ASN1EncoderUpdate(p7ecx
->ecx
, NULL
, 0);
757 result
= PORT_GetError();
760 SEC_ASN1EncoderClearStreaming(p7ecx
->ecx
);
762 if (p7ecx
->error
&& !result
)
763 result
= p7ecx
->error
;
766 SEC_ASN1EncoderFinish(p7ecx
->ecx
);
768 PORT_SetError(0); // Clean the thread error since we've returned the error
773 SecCmsMessageEncode(SecCmsMessageRef cmsg
, const CSSM_DATA
*input
, SecArenaPoolRef arena
,
774 CSSM_DATA_PTR outBer
)
776 SecCmsEncoderRef encoder
;
779 if (!cmsg
|| !outBer
|| !arena
) {
784 result
= SecCmsEncoderCreate(cmsg
, 0, 0, outBer
, arena
, 0, 0, 0, 0, 0, 0, &encoder
);
789 result
= SecCmsEncoderUpdate(encoder
, input
->Data
, input
->Length
);
791 SecCmsEncoderDestroy(encoder
);
795 result
= SecCmsEncoderFinish(encoder
);