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
37
* $Id: cmsencode.c,v 1.4 2003/05/30 23:31:27 wtc%netscape.com Exp $
50
struct nss_cms_encoder_output {
51
NSSCMSContentCallback outputfn;
53
PLArenaPool *destpoolp;
57
struct NSSCMSEncoderContextStr {
58
SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
59
PRBool ecxupdated; /* true if data was handed in */
60
NSSCMSMessage * cmsg; /* pointer to the root message */
61
SECOidTag type; /* type tag of the current content */
62
NSSCMSContent content; /* pointer to current content */
63
struct nss_cms_encoder_output output; /* output function */
64
int error; /* error code */
65
NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */
68
static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
69
static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
70
static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
71
static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
72
const unsigned char *data, unsigned long len,
73
PRBool final, PRBool innermost);
75
extern const SEC_ASN1Template NSSCMSMessageTemplate[];
78
* The little output function that the ASN.1 encoder calls to hand
79
* us bytes which we in turn hand back to our caller (via the callback
83
nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
84
int depth, SEC_ASN1EncodingPart data_kind)
86
struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
93
fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len);
94
for (i=0; i < len; i++) {
95
fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
98
fprintf(stderr, "\n");
101
if (output->outputfn != NULL)
102
/* call output callback with DER data */
103
output->outputfn(output->outputarg, buf, len);
105
if (output->dest != NULL) {
106
/* store DER data in SECItem */
107
offset = output->dest->len;
109
dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
111
dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
114
output->dest->len + len);
120
output->dest->data = dest;
121
output->dest->len += len;
124
PORT_Memcpy(output->dest->data + offset, buf, len);
129
* nss_cms_encoder_notify - ASN.1 encoder callback
131
* this function is called by the ASN.1 encoder before and after the encoding of
132
* every object. here, it is used to keep track of data structures, set up
133
* encryption and/or digesting and possibly set up child encoders.
136
nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
138
NSSCMSEncoderContext *p7ecx;
139
NSSCMSContentInfo *rootcinfo, *cinfo;
140
PRBool after = !before;
145
p7ecx = (NSSCMSEncoderContext *)arg;
146
PORT_Assert(p7ecx != NULL);
148
rootcinfo = &(p7ecx->cmsg->contentInfo);
149
poolp = p7ecx->cmsg->poolp;
152
fprintf(stderr, "%6.6s, dest = 0x%08x, 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 = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
166
/* set up a pointer to our current content */
167
p7ecx->content = rootcinfo->content;
171
case SEC_OID_PKCS7_DATA:
172
if (before && dest == &(rootcinfo->rawContent)) {
173
/* just set up encoder to grab from user - no encryption or digesting */
174
if ((item = rootcinfo->content.data) != NULL)
175
(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
177
SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
178
SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
182
case SEC_OID_PKCS7_SIGNED_DATA:
183
case SEC_OID_PKCS7_ENVELOPED_DATA:
184
case SEC_OID_PKCS7_DIGESTED_DATA:
185
case SEC_OID_PKCS7_ENCRYPTED_DATA:
187
/* when we know what the content is, we encode happily until we reach the inner content */
188
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
189
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
191
if (after && dest == &(cinfo->contentType)) {
192
/* we're right before encoding the data (if we have some or not) */
193
/* (for encrypted data, we're right before the contentEncAlg which may change */
194
/* in nss_cms_before_data because of IV calculation when setting up encryption) */
195
if (nss_cms_before_data(p7ecx) != SECSuccess)
196
p7ecx->error = PORT_GetError();
198
if (before && dest == &(cinfo->rawContent)) {
199
if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
200
/* we have data - feed it in */
201
(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
203
/* else try to get it from user */
204
SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
206
if (after && dest == &(cinfo->rawContent)) {
207
if (nss_cms_after_data(p7ecx) != SECSuccess)
208
p7ecx->error = PORT_GetError();
209
SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
216
* nss_cms_before_data - setup the current encoder to receive data
219
nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
223
NSSCMSContentInfo *cinfo;
225
NSSCMSEncoderContext *childp7ecx;
226
const SEC_ASN1Template *template;
228
poolp = p7ecx->cmsg->poolp;
230
/* call _Encode_BeforeData handlers */
231
switch (p7ecx->type) {
232
case SEC_OID_PKCS7_SIGNED_DATA:
233
/* we're encoding a signedData, so set up the digests */
234
rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
236
case SEC_OID_PKCS7_DIGESTED_DATA:
237
/* we're encoding a digestedData, so set up the digest */
238
rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
240
case SEC_OID_PKCS7_ENVELOPED_DATA:
241
rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
243
case SEC_OID_PKCS7_ENCRYPTED_DATA:
244
rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
249
if (rv != SECSuccess)
252
/* ok, now we have a pointer to cinfo */
253
/* find out what kind of data is encapsulated */
255
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
256
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
259
case SEC_OID_PKCS7_SIGNED_DATA:
260
case SEC_OID_PKCS7_ENVELOPED_DATA:
261
case SEC_OID_PKCS7_ENCRYPTED_DATA:
262
case SEC_OID_PKCS7_DIGESTED_DATA:
264
case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */
266
/* in these cases, we need to set up a child encoder! */
267
/* create new encoder context */
268
childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
269
if (childp7ecx == NULL)
272
/* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
273
* (which will encrypt and/or digest it)
274
* this needs to route back into our update function
275
* which finds the lowest encoding context & encrypts and computes digests */
276
childp7ecx->type = childtype;
277
childp7ecx->content = cinfo->content;
278
/* use the non-recursive update function here, of course */
279
childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
280
childp7ecx->output.outputarg = p7ecx;
281
childp7ecx->output.destpoolp = NULL;
282
childp7ecx->output.dest = NULL;
283
childp7ecx->cmsg = p7ecx->cmsg;
285
template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
286
if (template == NULL)
287
goto loser; /* cannot happen */
289
/* now initialize the data for encoding the first third */
290
switch (childp7ecx->type) {
291
case SEC_OID_PKCS7_SIGNED_DATA:
292
rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
294
case SEC_OID_PKCS7_ENVELOPED_DATA:
295
rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
297
case SEC_OID_PKCS7_DIGESTED_DATA:
298
rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
300
case SEC_OID_PKCS7_ENCRYPTED_DATA:
301
rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
303
case SEC_OID_PKCS7_DATA:
310
if (rv != SECSuccess)
314
* Initialize the BER encoder.
316
childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
317
nss_cms_encoder_out, &(childp7ecx->output));
318
if (childp7ecx->ecx == NULL)
321
childp7ecx->ecxupdated = PR_FALSE;
324
* Indicate that we are streaming. We will be streaming until we
325
* get past the contents bytes.
327
SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
330
* The notify function will watch for the contents field.
332
SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
334
/* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
335
/* encoding process - we'll do that from the update function instead */
336
/* otherwise we'd be encoding data from a call of the notify function of the */
337
/* parent encoder (which would not work) */
339
/* this will kick off the encoding process & encode everything up to the content bytes,
340
* at which point the notify function sets streaming mode (and possibly creates
341
* another child encoder). */
342
if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
345
p7ecx->childp7ecx = childp7ecx;
348
case SEC_OID_PKCS7_DATA:
349
p7ecx->childp7ecx = NULL;
352
/* we do not know this type */
353
p7ecx->error = SEC_ERROR_BAD_DER;
362
SEC_ASN1EncoderFinish(childp7ecx->ecx);
363
PORT_Free(childp7ecx);
369
nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
371
SECStatus rv = SECFailure;
373
switch (p7ecx->type) {
374
case SEC_OID_PKCS7_SIGNED_DATA:
375
/* this will finish the digests and sign */
376
rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
378
case SEC_OID_PKCS7_ENVELOPED_DATA:
379
rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
381
case SEC_OID_PKCS7_DIGESTED_DATA:
382
rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
384
case SEC_OID_PKCS7_ENCRYPTED_DATA:
385
rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
387
case SEC_OID_PKCS7_DATA:
398
* nss_cms_encoder_work_data - process incoming data
400
* (from the user or the next encoding layer)
401
* Here, we need to digest and/or encrypt, then pass it on
404
nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
405
const unsigned char *data, unsigned long len,
406
PRBool final, PRBool innermost)
408
unsigned char *buf = NULL;
410
NSSCMSContentInfo *cinfo;
412
rv = SECSuccess; /* may as well be optimistic */
415
* We should really have data to process, or we should be trying
416
* to finish/flush the last block. (This is an overly paranoid
417
* check since all callers are in this file and simple inspection
418
* proves they do it right. But it could find a bug in future
419
* modifications/development, that is why it is here.)
421
PORT_Assert ((data != NULL && len) || final);
423
/* we got data (either from the caller, or from a lower level encoder) */
424
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
426
/* Update the running digest. */
427
if (len && cinfo->digcx != NULL)
428
NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
430
/* Encrypt this chunk. */
431
if (cinfo->ciphcx != NULL) {
432
unsigned int inlen; /* length of data being encrypted */
433
unsigned int outlen; /* length of encrypted data */
434
unsigned int buflen; /* length available for encrypted data */
437
buflen = NSS_CMSCipherContext_EncryptLength(cinfo->ciphcx, inlen, final);
440
* No output is expected, but the input data may be buffered
441
* so we still have to call Encrypt.
443
rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, NULL, NULL, 0,
453
buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
455
buf = (unsigned char*)PORT_Alloc(buflen);
460
rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, buf, &outlen, buflen,
465
if (rv != SECSuccess)
466
/* encryption or malloc failed? */
472
* at this point (data,len) has everything we'd like to give to the CURRENT encoder
473
* (which will encode it, then hand it back to the user or the parent encoder)
474
* We don't encode the data if we're innermost and we're told not to include the data
476
if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
477
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
481
if (cinfo->ciphcx != NULL) {
485
} else if (buf != NULL) {
493
* nss_cms_encoder_update - deliver encoded data to the next higher level
495
* no recursion here because we REALLY want to end up at the next higher encoder!
498
nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
500
/* XXX Error handling needs help. Return what? Do "Finish" on failure? */
501
return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
505
* NSS_CMSEncoder_Start - set up encoding of a CMS message
507
* "cmsg" - message to encode
508
* "outputfn", "outputarg" - callback function for delivery of DER-encoded output
509
* will not be called if NULL.
510
* "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
511
* "destpoolp" - pool to allocate DER-encoded output in
512
* "pwfn", pwfn_arg" - callback function for getting token password
513
* "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
514
* "detached_digestalgs", "detached_digests" - digests from detached content
516
NSSCMSEncoderContext *
517
NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
518
NSSCMSContentCallback outputfn, void *outputarg,
519
SECItem *dest, PLArenaPool *destpoolp,
520
PK11PasswordFunc pwfn, void *pwfn_arg,
521
NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
522
SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
524
NSSCMSEncoderContext *p7ecx;
526
NSSCMSContentInfo *cinfo;
528
NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
529
detached_digestalgs, detached_digests);
531
p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
533
PORT_SetError(SEC_ERROR_NO_MEMORY);
538
p7ecx->output.outputfn = outputfn;
539
p7ecx->output.outputarg = outputarg;
540
p7ecx->output.dest = dest;
541
p7ecx->output.destpoolp = destpoolp;
542
p7ecx->type = SEC_OID_UNKNOWN;
544
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
546
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
547
case SEC_OID_PKCS7_SIGNED_DATA:
548
rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
550
case SEC_OID_PKCS7_ENVELOPED_DATA:
551
rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
553
case SEC_OID_PKCS7_DIGESTED_DATA:
554
rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
556
case SEC_OID_PKCS7_ENCRYPTED_DATA:
557
rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
563
if (rv != SECSuccess)
566
/* Initialize the BER encoder.
567
* Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
568
p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
569
nss_cms_encoder_out, &(p7ecx->output));
570
if (p7ecx->ecx == NULL) {
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) {
599
* NSS_CMSEncoder_Update - take content data delivery from the user
601
* "p7ecx" - encoder context
602
* "data" - content data
603
* "len" - length of content data
605
* need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
606
* then hand the data to the work_data fn
609
NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
612
NSSCMSContentInfo *cinfo;
618
/* hand data to the innermost decoder */
619
if (p7ecx->childp7ecx) {
621
rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
623
/* we are at innermost decoder */
624
/* find out about our inner content type - must be data */
625
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
626
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
627
if (childtype != SEC_OID_PKCS7_DATA)
629
/* and we must not have preset data */
630
if (cinfo->content.data != NULL)
633
/* hand it the data so it can encode it (let DER trickle up the chain) */
634
rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
640
* NSS_CMSEncoder_Cancel - stop all encoding
642
* we need to walk down the chain of encoders and the finish them from the innermost out
645
NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
647
SECStatus rv = SECFailure;
649
/* XXX do this right! */
652
* Finish any inner decoders before us so that all the encoded data is flushed
653
* This basically finishes all the decoders from the innermost to the outermost.
654
* Finishing an inner decoder may result in data being updated to the outer decoder
655
* while we are already in NSS_CMSEncoder_Finish, but that's allright.
657
if (p7ecx->childp7ecx) {
658
rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
659
/* remember rv for now */
663
* On the way back up, there will be no more data (if we had an
664
* inner encoder, it is done now!)
665
* Flush out any remaining data and/or finish digests.
667
rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
668
if (rv != SECSuccess)
671
p7ecx->childp7ecx = NULL;
673
/* kick the encoder back into working mode again.
674
* We turn off streaming stuff (which will cause the encoder to continue
675
* encoding happily, now that we have all the data (like digests) ready for it).
677
SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
678
SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
680
/* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
681
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
684
SEC_ASN1EncoderFinish(p7ecx->ecx);
690
* NSS_CMSEncoder_Finish - signal the end of data
692
* we need to walk down the chain of encoders and the finish them from the innermost out
695
NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
697
SECStatus rv = SECFailure;
698
NSSCMSContentInfo *cinfo;
702
* Finish any inner decoders before us so that all the encoded data is flushed
703
* This basically finishes all the decoders from the innermost to the outermost.
704
* Finishing an inner decoder may result in data being updated to the outer decoder
705
* while we are already in NSS_CMSEncoder_Finish, but that's allright.
707
if (p7ecx->childp7ecx) {
708
rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
709
if (rv != SECSuccess)
714
* On the way back up, there will be no more data (if we had an
715
* inner encoder, it is done now!)
716
* Flush out any remaining data and/or finish digests.
718
rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
719
if (rv != SECSuccess)
722
p7ecx->childp7ecx = NULL;
724
/* find out about our inner content type - must be data */
725
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
726
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
727
if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
728
SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
729
/* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
730
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
733
SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
739
SEC_ASN1EncoderFinish(p7ecx->ecx);