Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / lib / smime / cmsencode.c
blob8719d27581f4360d78eab39d879d55ec251022d4
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 * CMS encoding.
40 * $Id: cmsencode.c,v 1.6 2006/06/08 22:01:02 nelson%bolyard.com Exp $
43 #include "cmslocal.h"
45 #include "cert.h"
46 #include "key.h"
47 #include "secasn1.h"
48 #include "secoid.h"
49 #include "secitem.h"
50 #include "pk11func.h"
51 #include "secerr.h"
53 struct nss_cms_encoder_output {
54 NSSCMSContentCallback outputfn;
55 void *outputarg;
56 PLArenaPool *destpoolp;
57 SECItem *dest;
60 struct NSSCMSEncoderContextStr {
61 SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
62 PRBool ecxupdated; /* true if data was handed in */
63 NSSCMSMessage * cmsg; /* pointer to the root message */
64 SECOidTag type; /* type tag of the current content */
65 NSSCMSContent content; /* pointer to current content */
66 struct nss_cms_encoder_output output; /* output function */
67 int error; /* error code */
68 NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */
71 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
72 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
73 static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
74 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
75 const unsigned char *data, unsigned long len,
76 PRBool final, PRBool innermost);
78 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
81 * The little output function that the ASN.1 encoder calls to hand
82 * us bytes which we in turn hand back to our caller (via the callback
83 * they gave us).
85 static void
86 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
87 int depth, SEC_ASN1EncodingPart data_kind)
89 struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
90 unsigned char *dest;
91 unsigned long offset;
93 #ifdef CMSDEBUG
94 int i;
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" : "");
100 if ((i % 16) != 0)
101 fprintf(stderr, "\n");
102 #endif
104 if (output->outputfn != NULL)
105 /* call output callback with DER data */
106 output->outputfn(output->outputarg, buf, len);
108 if (output->dest != NULL) {
109 /* store DER data in SECItem */
110 offset = output->dest->len;
111 if (offset == 0) {
112 dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
113 } else {
114 dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
115 output->dest->data,
116 output->dest->len,
117 output->dest->len + len);
119 if (dest == NULL)
120 /* oops */
121 return;
123 output->dest->data = dest;
124 output->dest->len += len;
126 /* copy it in */
127 PORT_Memcpy(output->dest->data + offset, buf, len);
132 * nss_cms_encoder_notify - ASN.1 encoder callback
134 * this function is called by the ASN.1 encoder before and after the encoding of
135 * every object. here, it is used to keep track of data structures, set up
136 * encryption and/or digesting and possibly set up child encoders.
138 static void
139 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
141 NSSCMSEncoderContext *p7ecx;
142 NSSCMSContentInfo *rootcinfo, *cinfo;
143 PRBool after = !before;
144 PLArenaPool *poolp;
145 SECOidTag childtype;
146 SECItem *item;
148 p7ecx = (NSSCMSEncoderContext *)arg;
149 PORT_Assert(p7ecx != NULL);
151 rootcinfo = &(p7ecx->cmsg->contentInfo);
152 poolp = p7ecx->cmsg->poolp;
154 #ifdef CMSDEBUG
155 fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
156 #endif
159 * Watch for the content field, at which point we want to instruct
160 * the ASN.1 encoder to start taking bytes from the buffer.
162 switch (p7ecx->type) {
163 default:
164 case SEC_OID_UNKNOWN:
165 /* we're still in the root message */
166 if (after && dest == &(rootcinfo->contentType)) {
167 /* got the content type OID now - so find out the type tag */
168 p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
169 /* set up a pointer to our current content */
170 p7ecx->content = rootcinfo->content;
172 break;
174 case SEC_OID_PKCS7_DATA:
175 if (before && dest == &(rootcinfo->rawContent)) {
176 /* just set up encoder to grab from user - no encryption or digesting */
177 if ((item = rootcinfo->content.data) != NULL)
178 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
179 else
180 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
181 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
183 break;
185 case SEC_OID_PKCS7_SIGNED_DATA:
186 case SEC_OID_PKCS7_ENVELOPED_DATA:
187 case SEC_OID_PKCS7_DIGESTED_DATA:
188 case SEC_OID_PKCS7_ENCRYPTED_DATA:
190 /* when we know what the content is, we encode happily until we reach the inner content */
191 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
192 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
194 if (after && dest == &(cinfo->contentType)) {
195 /* we're right before encoding the data (if we have some or not) */
196 /* (for encrypted data, we're right before the contentEncAlg which may change */
197 /* in nss_cms_before_data because of IV calculation when setting up encryption) */
198 if (nss_cms_before_data(p7ecx) != SECSuccess)
199 p7ecx->error = PORT_GetError();
201 if (before && dest == &(cinfo->rawContent)) {
202 if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
203 /* we have data - feed it in */
204 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
205 else
206 /* else try to get it from user */
207 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
209 if (after && dest == &(cinfo->rawContent)) {
210 if (nss_cms_after_data(p7ecx) != SECSuccess)
211 p7ecx->error = PORT_GetError();
212 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
214 break;
219 * nss_cms_before_data - setup the current encoder to receive data
221 static SECStatus
222 nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
224 SECStatus rv;
225 SECOidTag childtype;
226 NSSCMSContentInfo *cinfo;
227 PLArenaPool *poolp;
228 NSSCMSEncoderContext *childp7ecx;
229 const SEC_ASN1Template *template;
231 poolp = p7ecx->cmsg->poolp;
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 = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
238 break;
239 case SEC_OID_PKCS7_DIGESTED_DATA:
240 /* we're encoding a digestedData, so set up the digest */
241 rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
242 break;
243 case SEC_OID_PKCS7_ENVELOPED_DATA:
244 rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
245 break;
246 case SEC_OID_PKCS7_ENCRYPTED_DATA:
247 rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
248 break;
249 default:
250 rv = SECFailure;
252 if (rv != SECSuccess)
253 return SECFailure;
255 /* ok, now we have a pointer to cinfo */
256 /* find out what kind of data is encapsulated */
258 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
259 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
261 switch (childtype) {
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:
266 #if 0
267 case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */
268 #endif
269 /* in these cases, we need to set up a child encoder! */
270 /* create new encoder context */
271 childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
272 if (childp7ecx == NULL)
273 return SECFailure;
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 = (NSSCMSContentCallback)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 = NSS_CMSUtil_GetTemplateByTypeTag(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 = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
296 break;
297 case SEC_OID_PKCS7_ENVELOPED_DATA:
298 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
299 break;
300 case SEC_OID_PKCS7_DIGESTED_DATA:
301 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
302 break;
303 case SEC_OID_PKCS7_ENCRYPTED_DATA:
304 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
305 break;
306 case SEC_OID_PKCS7_DATA:
307 rv = SECSuccess;
308 break;
309 default:
310 PORT_Assert(0);
311 break;
313 if (rv != SECSuccess)
314 goto loser;
317 * Initialize the BER encoder.
319 childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
320 nss_cms_encoder_out, &(childp7ecx->output));
321 if (childp7ecx->ecx == NULL)
322 goto loser;
324 childp7ecx->ecxupdated = PR_FALSE;
327 * Indicate that we are streaming. We will be streaming until we
328 * get past the contents bytes.
330 SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
333 * The notify function will watch for the contents field.
335 SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
337 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
338 /* encoding process - we'll do that from the update function instead */
339 /* otherwise we'd be encoding data from a call of the notify function of the */
340 /* parent encoder (which would not work) */
342 /* this will kick off the encoding process & encode everything up to the content bytes,
343 * at which point the notify function sets streaming mode (and possibly creates
344 * another child encoder). */
345 if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
346 goto loser;
348 p7ecx->childp7ecx = childp7ecx;
349 break;
351 case SEC_OID_PKCS7_DATA:
352 p7ecx->childp7ecx = NULL;
353 break;
354 default:
355 /* we do not know this type */
356 p7ecx->error = SEC_ERROR_BAD_DER;
357 break;
360 return SECSuccess;
362 loser:
363 if (childp7ecx) {
364 if (childp7ecx->ecx)
365 SEC_ASN1EncoderFinish(childp7ecx->ecx);
366 PORT_Free(childp7ecx);
368 return SECFailure;
371 static SECStatus
372 nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
374 SECStatus rv = SECFailure;
376 switch (p7ecx->type) {
377 case SEC_OID_PKCS7_SIGNED_DATA:
378 /* this will finish the digests and sign */
379 rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
380 break;
381 case SEC_OID_PKCS7_ENVELOPED_DATA:
382 rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
383 break;
384 case SEC_OID_PKCS7_DIGESTED_DATA:
385 rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
386 break;
387 case SEC_OID_PKCS7_ENCRYPTED_DATA:
388 rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
389 break;
390 case SEC_OID_PKCS7_DATA:
391 /* do nothing */
392 break;
393 default:
394 rv = SECFailure;
395 break;
397 return rv;
401 * nss_cms_encoder_work_data - process incoming data
403 * (from the user or the next encoding layer)
404 * Here, we need to digest and/or encrypt, then pass it on
406 static SECStatus
407 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
408 const unsigned char *data, unsigned long len,
409 PRBool final, PRBool innermost)
411 unsigned char *buf = NULL;
412 SECStatus rv;
413 NSSCMSContentInfo *cinfo;
415 rv = SECSuccess; /* may as well be optimistic */
418 * We should really have data to process, or we should be trying
419 * to finish/flush the last block. (This is an overly paranoid
420 * check since all callers are in this file and simple inspection
421 * proves they do it right. But it could find a bug in future
422 * modifications/development, that is why it is here.)
424 PORT_Assert ((data != NULL && len) || final);
426 /* we got data (either from the caller, or from a lower level encoder) */
427 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
429 /* Update the running digest. */
430 if (len && cinfo->digcx != NULL)
431 NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
433 /* Encrypt this chunk. */
434 if (cinfo->ciphcx != NULL) {
435 unsigned int inlen; /* length of data being encrypted */
436 unsigned int outlen; /* length of encrypted data */
437 unsigned int buflen; /* length available for encrypted data */
439 inlen = len;
440 buflen = NSS_CMSCipherContext_EncryptLength(cinfo->ciphcx, inlen, final);
441 if (buflen == 0) {
443 * No output is expected, but the input data may be buffered
444 * so we still have to call Encrypt.
446 rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, NULL, NULL, 0,
447 data, inlen, final);
448 if (final) {
449 len = 0;
450 goto done;
452 return rv;
455 if (dest != NULL)
456 buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
457 else
458 buf = (unsigned char*)PORT_Alloc(buflen);
460 if (buf == NULL) {
461 rv = SECFailure;
462 } else {
463 rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, buf, &outlen, buflen,
464 data, inlen, final);
465 data = buf;
466 len = outlen;
468 if (rv != SECSuccess)
469 /* encryption or malloc failed? */
470 return rv;
475 * at this point (data,len) has everything we'd like to give to the CURRENT encoder
476 * (which will encode it, then hand it back to the user or the parent encoder)
477 * We don't encode the data if we're innermost and we're told not to include the data
479 if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
480 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
482 done:
484 if (cinfo->ciphcx != NULL) {
485 if (dest != NULL) {
486 dest->data = buf;
487 dest->len = len;
488 } else if (buf != NULL) {
489 PORT_Free (buf);
492 return rv;
496 * nss_cms_encoder_update - deliver encoded data to the next higher level
498 * no recursion here because we REALLY want to end up at the next higher encoder!
500 static SECStatus
501 nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
503 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
504 return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
508 * NSS_CMSEncoder_Start - set up encoding of a CMS message
510 * "cmsg" - message to encode
511 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
512 * will not be called if NULL.
513 * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
514 * "destpoolp" - pool to allocate DER-encoded output in
515 * "pwfn", pwfn_arg" - callback function for getting token password
516 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
517 * "detached_digestalgs", "detached_digests" - digests from detached content
519 NSSCMSEncoderContext *
520 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
521 NSSCMSContentCallback outputfn, void *outputarg,
522 SECItem *dest, PLArenaPool *destpoolp,
523 PK11PasswordFunc pwfn, void *pwfn_arg,
524 NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
525 SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
527 NSSCMSEncoderContext *p7ecx;
528 SECStatus rv;
529 NSSCMSContentInfo *cinfo;
531 NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
532 detached_digestalgs, detached_digests);
534 p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
535 if (p7ecx == NULL) {
536 PORT_SetError(SEC_ERROR_NO_MEMORY);
537 return NULL;
540 p7ecx->cmsg = cmsg;
541 p7ecx->output.outputfn = outputfn;
542 p7ecx->output.outputarg = outputarg;
543 p7ecx->output.dest = dest;
544 p7ecx->output.destpoolp = destpoolp;
545 p7ecx->type = SEC_OID_UNKNOWN;
547 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
549 switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
550 case SEC_OID_PKCS7_SIGNED_DATA:
551 rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
552 break;
553 case SEC_OID_PKCS7_ENVELOPED_DATA:
554 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
555 break;
556 case SEC_OID_PKCS7_DIGESTED_DATA:
557 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
558 break;
559 case SEC_OID_PKCS7_ENCRYPTED_DATA:
560 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
561 break;
562 default:
563 rv = SECFailure;
564 break;
566 if (rv != SECSuccess) {
567 PORT_Free(p7ecx);
568 return NULL;
571 /* Initialize the BER encoder.
572 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
573 p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
574 nss_cms_encoder_out, &(p7ecx->output));
575 if (p7ecx->ecx == NULL) {
576 PORT_Free (p7ecx);
577 return NULL;
579 p7ecx->ecxupdated = PR_FALSE;
582 * Indicate that we are streaming. We will be streaming until we
583 * get past the contents bytes.
585 SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
588 * The notify function will watch for the contents field.
590 SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
592 /* this will kick off the encoding process & encode everything up to the content bytes,
593 * at which point the notify function sets streaming mode (and possibly creates
594 * a child encoder). */
595 if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
596 PORT_Free (p7ecx);
597 return NULL;
600 return p7ecx;
604 * NSS_CMSEncoder_Update - take content data delivery from the user
606 * "p7ecx" - encoder context
607 * "data" - content data
608 * "len" - length of content data
610 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
611 * then hand the data to the work_data fn
613 SECStatus
614 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
616 SECStatus rv;
617 NSSCMSContentInfo *cinfo;
618 SECOidTag childtype;
620 if (p7ecx->error)
621 return SECFailure;
623 /* hand data to the innermost decoder */
624 if (p7ecx->childp7ecx) {
625 /* recursion here */
626 rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
627 } else {
628 /* we are at innermost decoder */
629 /* find out about our inner content type - must be data */
630 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
631 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
632 if (childtype != SEC_OID_PKCS7_DATA)
633 return SECFailure;
634 /* and we must not have preset data */
635 if (cinfo->content.data != NULL)
636 return SECFailure;
638 /* hand it the data so it can encode it (let DER trickle up the chain) */
639 rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
641 return rv;
645 * NSS_CMSEncoder_Cancel - stop all encoding
647 * we need to walk down the chain of encoders and the finish them from the innermost out
649 SECStatus
650 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
652 SECStatus rv = SECFailure;
654 /* XXX do this right! */
657 * Finish any inner decoders before us so that all the encoded data is flushed
658 * This basically finishes all the decoders from the innermost to the outermost.
659 * Finishing an inner decoder may result in data being updated to the outer decoder
660 * while we are already in NSS_CMSEncoder_Finish, but that's allright.
662 if (p7ecx->childp7ecx) {
663 rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
664 /* remember rv for now */
668 * On the way back up, there will be no more data (if we had an
669 * inner encoder, it is done now!)
670 * Flush out any remaining data and/or finish digests.
672 rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
673 if (rv != SECSuccess)
674 goto loser;
676 p7ecx->childp7ecx = NULL;
678 /* kick the encoder back into working mode again.
679 * We turn off streaming stuff (which will cause the encoder to continue
680 * encoding happily, now that we have all the data (like digests) ready for it).
682 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
683 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
685 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
686 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
688 loser:
689 SEC_ASN1EncoderFinish(p7ecx->ecx);
690 PORT_Free (p7ecx);
691 return rv;
695 * NSS_CMSEncoder_Finish - signal the end of data
697 * we need to walk down the chain of encoders and the finish them from the innermost out
699 SECStatus
700 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
702 SECStatus rv = SECFailure;
703 NSSCMSContentInfo *cinfo;
704 SECOidTag childtype;
707 * Finish any inner decoders before us so that all the encoded data is flushed
708 * This basically finishes all the decoders from the innermost to the outermost.
709 * Finishing an inner decoder may result in data being updated to the outer decoder
710 * while we are already in NSS_CMSEncoder_Finish, but that's allright.
712 if (p7ecx->childp7ecx) {
713 rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
714 if (rv != SECSuccess)
715 goto loser;
719 * On the way back up, there will be no more data (if we had an
720 * inner encoder, it is done now!)
721 * Flush out any remaining data and/or finish digests.
723 rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
724 if (rv != SECSuccess)
725 goto loser;
727 p7ecx->childp7ecx = NULL;
729 /* find out about our inner content type - must be data */
730 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
731 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
732 if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
733 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
734 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
735 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
738 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
740 if (p7ecx->error)
741 rv = SECFailure;
743 loser:
744 SEC_ASN1EncoderFinish(p7ecx->ecx);
745 PORT_Free (p7ecx);
746 return rv;