Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /*
6 * CMS encoding.
7 */
9 #include "cmslocal.h"
11 #include "cert.h"
12 #include "key.h"
13 #include "secasn1.h"
14 #include "secoid.h"
15 #include "secitem.h"
16 #include "pk11func.h"
17 #include "secerr.h"
19 struct nss_cms_encoder_output {
20 NSSCMSContentCallback outputfn;
21 void *outputarg;
22 PLArenaPool *destpoolp;
23 SECItem *dest;
24 };
26 struct NSSCMSEncoderContextStr {
27 SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
28 PRBool ecxupdated; /* true if data was handed in */
29 NSSCMSMessage * cmsg; /* pointer to the root message */
30 SECOidTag type; /* type tag of the current content */
31 NSSCMSContent content; /* pointer to current content */
32 struct nss_cms_encoder_output output; /* output function */
33 int error; /* error code */
34 NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */
35 };
37 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
38 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
39 static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
40 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
41 const unsigned char *data, unsigned long len,
42 PRBool final, PRBool innermost);
44 extern const SEC_ASN1Template NSSCMSMessageTemplate[];
46 /*
47 * The little output function that the ASN.1 encoder calls to hand
48 * us bytes which we in turn hand back to our caller (via the callback
49 * they gave us).
50 */
51 static void
52 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
53 int depth, SEC_ASN1EncodingPart data_kind)
54 {
55 struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
56 unsigned char *dest;
57 unsigned long offset;
59 #ifdef CMSDEBUG
60 int i;
61 const char *data_name = "unknown";
63 switch (data_kind) {
64 case SEC_ASN1_Identifier:
65 data_name = "identifier";
66 break;
67 case SEC_ASN1_Length:
68 data_name = "length";
69 break;
70 case SEC_ASN1_Contents:
71 data_name = "contents";
72 break;
73 case SEC_ASN1_EndOfContents:
74 data_name = "end-of-contents";
75 break;
76 }
77 fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len);
78 for (i=0; i < len; i++) {
79 fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
80 }
81 if ((i % 16) != 0)
82 fprintf(stderr, "\n");
83 #endif
85 if (output->outputfn != NULL)
86 /* call output callback with DER data */
87 output->outputfn(output->outputarg, buf, len);
89 if (output->dest != NULL) {
90 /* store DER data in SECItem */
91 offset = output->dest->len;
92 if (offset == 0) {
93 dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
94 } else {
95 dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
96 output->dest->data,
97 output->dest->len,
98 output->dest->len + len);
99 }
100 if (dest == NULL)
101 /* oops */
102 return;
104 output->dest->data = dest;
105 output->dest->len += len;
107 /* copy it in */
108 PORT_Memcpy(output->dest->data + offset, buf, len);
109 }
110 }
112 /*
113 * nss_cms_encoder_notify - ASN.1 encoder callback
114 *
115 * this function is called by the ASN.1 encoder before and after the encoding of
116 * every object. here, it is used to keep track of data structures, set up
117 * encryption and/or digesting and possibly set up child encoders.
118 */
119 static void
120 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
121 {
122 NSSCMSEncoderContext *p7ecx;
123 NSSCMSContentInfo *rootcinfo, *cinfo;
124 PRBool after = !before;
125 PLArenaPool *poolp;
126 SECOidTag childtype;
127 SECItem *item;
129 p7ecx = (NSSCMSEncoderContext *)arg;
130 PORT_Assert(p7ecx != NULL);
132 rootcinfo = &(p7ecx->cmsg->contentInfo);
133 poolp = p7ecx->cmsg->poolp;
135 #ifdef CMSDEBUG
136 fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
137 #endif
139 /*
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.
142 */
143 if (NSS_CMSType_IsData(p7ecx->type)) {
144 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
145 if (before && dest == &(cinfo->rawContent)) {
146 /* just set up encoder to grab from user - no encryption or digesting */
147 if ((item = cinfo->content.data) != NULL)
148 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
149 else
150 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
151 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
152 }
153 } else if (NSS_CMSType_IsWrapper(p7ecx->type)) {
154 /* when we know what the content is, we encode happily until we reach the inner content */
155 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
156 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
158 if (after && dest == &(cinfo->contentType)) {
159 /* we're right before encoding the data (if we have some or not) */
160 /* (for encrypted data, we're right before the contentEncAlg which may change */
161 /* in nss_cms_before_data because of IV calculation when setting up encryption) */
162 if (nss_cms_before_data(p7ecx) != SECSuccess)
163 p7ecx->error = PORT_GetError();
164 }
165 if (before && dest == &(cinfo->rawContent)) {
166 if (p7ecx->childp7ecx == NULL) {
167 if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) {
168 /* we are the innermost non-data and we have data - feed it in */
169 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
170 } else {
171 /* else we'll have to get data from user */
172 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
173 }
174 } else {
175 /* if we have a nested encoder, wait for its data */
176 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
177 }
178 }
179 if (after && dest == &(cinfo->rawContent)) {
180 if (nss_cms_after_data(p7ecx) != SECSuccess)
181 p7ecx->error = PORT_GetError();
182 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
183 }
184 } else {
185 /* we're still in the root message */
186 if (after && dest == &(rootcinfo->contentType)) {
187 /* got the content type OID now - so find out the type tag */
188 p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
189 /* set up a pointer to our current content */
190 p7ecx->content = rootcinfo->content;
191 }
192 }
193 }
195 /*
196 * nss_cms_before_data - setup the current encoder to receive data
197 */
198 static SECStatus
199 nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
200 {
201 SECStatus rv;
202 SECOidTag childtype;
203 NSSCMSContentInfo *cinfo;
204 PLArenaPool *poolp;
205 NSSCMSEncoderContext *childp7ecx;
206 const SEC_ASN1Template *template;
208 poolp = p7ecx->cmsg->poolp;
210 /* call _Encode_BeforeData handlers */
211 switch (p7ecx->type) {
212 case SEC_OID_PKCS7_SIGNED_DATA:
213 /* we're encoding a signedData, so set up the digests */
214 rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
215 break;
216 case SEC_OID_PKCS7_DIGESTED_DATA:
217 /* we're encoding a digestedData, so set up the digest */
218 rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
219 break;
220 case SEC_OID_PKCS7_ENVELOPED_DATA:
221 rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
222 break;
223 case SEC_OID_PKCS7_ENCRYPTED_DATA:
224 rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
225 break;
226 default:
227 if (NSS_CMSType_IsWrapper(p7ecx->type)) {
228 rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, p7ecx->content.genericData);
229 } else {
230 rv = SECFailure;
231 }
232 }
233 if (rv != SECSuccess)
234 return SECFailure;
236 /* ok, now we have a pointer to cinfo */
237 /* find out what kind of data is encapsulated */
239 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
240 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
242 if (NSS_CMSType_IsWrapper(childtype)) {
243 /* in these cases, we need to set up a child encoder! */
244 /* create new encoder context */
245 childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
246 if (childp7ecx == NULL)
247 return SECFailure;
249 /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
250 * (which will encrypt and/or digest it)
251 * this needs to route back into our update function
252 * which finds the lowest encoding context & encrypts and computes digests */
253 childp7ecx->type = childtype;
254 childp7ecx->content = cinfo->content;
255 /* use the non-recursive update function here, of course */
256 childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
257 childp7ecx->output.outputarg = p7ecx;
258 childp7ecx->output.destpoolp = NULL;
259 childp7ecx->output.dest = NULL;
260 childp7ecx->cmsg = p7ecx->cmsg;
261 childp7ecx->ecxupdated = PR_FALSE;
262 childp7ecx->childp7ecx = NULL;
264 template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
265 if (template == NULL)
266 goto loser; /* cannot happen */
268 /* now initialize the data for encoding the first third */
269 switch (childp7ecx->type) {
270 case SEC_OID_PKCS7_SIGNED_DATA:
271 rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
272 break;
273 case SEC_OID_PKCS7_ENVELOPED_DATA:
274 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
275 break;
276 case SEC_OID_PKCS7_DIGESTED_DATA:
277 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
278 break;
279 case SEC_OID_PKCS7_ENCRYPTED_DATA:
280 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
281 break;
282 default:
283 rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, cinfo->content.genericData);
284 break;
285 }
286 if (rv != SECSuccess)
287 goto loser;
289 /*
290 * Initialize the BER encoder.
291 */
292 childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
293 nss_cms_encoder_out, &(childp7ecx->output));
294 if (childp7ecx->ecx == NULL)
295 goto loser;
297 /*
298 * Indicate that we are streaming. We will be streaming until we
299 * get past the contents bytes.
300 */
301 if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
302 SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
304 /*
305 * The notify function will watch for the contents field.
306 */
307 p7ecx->childp7ecx = childp7ecx;
308 SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
310 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
311 /* encoding process - we'll do that from the update function instead */
312 /* otherwise we'd be encoding data from a call of the notify function of the */
313 /* parent encoder (which would not work) */
315 } else if (NSS_CMSType_IsData(childtype)) {
316 p7ecx->childp7ecx = NULL;
317 } else {
318 /* we do not know this type */
319 p7ecx->error = SEC_ERROR_BAD_DER;
320 }
322 return SECSuccess;
324 loser:
325 if (childp7ecx) {
326 if (childp7ecx->ecx)
327 SEC_ASN1EncoderFinish(childp7ecx->ecx);
328 PORT_Free(childp7ecx);
329 p7ecx->childp7ecx = NULL;
330 }
331 return SECFailure;
332 }
334 static SECStatus
335 nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
336 {
337 SECStatus rv = SECFailure;
339 switch (p7ecx->type) {
340 case SEC_OID_PKCS7_SIGNED_DATA:
341 /* this will finish the digests and sign */
342 rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
343 break;
344 case SEC_OID_PKCS7_ENVELOPED_DATA:
345 rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
346 break;
347 case SEC_OID_PKCS7_DIGESTED_DATA:
348 rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
349 break;
350 case SEC_OID_PKCS7_ENCRYPTED_DATA:
351 rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
352 break;
353 default:
354 if (NSS_CMSType_IsWrapper(p7ecx->type)) {
355 rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, p7ecx->content.genericData);
356 } else {
357 rv = SECFailure;
358 }
359 break;
360 }
361 return rv;
362 }
364 /*
365 * nss_cms_encoder_work_data - process incoming data
366 *
367 * (from the user or the next encoding layer)
368 * Here, we need to digest and/or encrypt, then pass it on
369 */
370 static SECStatus
371 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
372 const unsigned char *data, unsigned long len,
373 PRBool final, PRBool innermost)
374 {
375 unsigned char *buf = NULL;
376 SECStatus rv;
377 NSSCMSContentInfo *cinfo;
379 rv = SECSuccess; /* may as well be optimistic */
381 /*
382 * We should really have data to process, or we should be trying
383 * to finish/flush the last block. (This is an overly paranoid
384 * check since all callers are in this file and simple inspection
385 * proves they do it right. But it could find a bug in future
386 * modifications/development, that is why it is here.)
387 */
388 PORT_Assert ((data != NULL && len) || final);
390 /* we got data (either from the caller, or from a lower level encoder) */
391 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
392 if (!cinfo) {
393 /* The original programmer didn't expect this to happen */
394 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
395 return SECFailure;
396 }
398 /* Update the running digest. */
399 if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL)
400 NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
402 /* Encrypt this chunk. */
403 if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
404 unsigned int inlen; /* length of data being encrypted */
405 unsigned int outlen; /* length of encrypted data */
406 unsigned int buflen; /* length available for encrypted data */
408 inlen = len;
409 buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, inlen, final);
410 if (buflen == 0) {
411 /*
412 * No output is expected, but the input data may be buffered
413 * so we still have to call Encrypt.
414 */
415 rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0,
416 data, inlen, final);
417 if (final) {
418 len = 0;
419 goto done;
420 }
421 return rv;
422 }
424 if (dest != NULL)
425 buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
426 else
427 buf = (unsigned char*)PORT_Alloc(buflen);
429 if (buf == NULL) {
430 rv = SECFailure;
431 } else {
432 rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
433 data, inlen, final);
434 data = buf;
435 len = outlen;
436 }
437 if (rv != SECSuccess)
438 /* encryption or malloc failed? */
439 return rv;
440 }
443 /*
444 * at this point (data,len) has everything we'd like to give to the CURRENT encoder
445 * (which will encode it, then hand it back to the user or the parent encoder)
446 * We don't encode the data if we're innermost and we're told not to include the data
447 */
448 if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != cinfo->content.pointer))
449 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
451 done:
453 if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
454 if (dest != NULL) {
455 dest->data = buf;
456 dest->len = len;
457 } else if (buf != NULL) {
458 PORT_Free (buf);
459 }
460 }
461 return rv;
462 }
464 /*
465 * nss_cms_encoder_update - deliver encoded data to the next higher level
466 *
467 * no recursion here because we REALLY want to end up at the next higher encoder!
468 */
469 static SECStatus
470 nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
471 {
472 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
473 return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
474 }
476 /*
477 * NSS_CMSEncoder_Start - set up encoding of a CMS message
478 *
479 * "cmsg" - message to encode
480 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
481 * will not be called if NULL.
482 * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
483 * "destpoolp" - pool to allocate DER-encoded output in
484 * "pwfn", pwfn_arg" - callback function for getting token password
485 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
486 * "detached_digestalgs", "detached_digests" - digests from detached content
487 */
488 NSSCMSEncoderContext *
489 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
490 NSSCMSContentCallback outputfn, void *outputarg,
491 SECItem *dest, PLArenaPool *destpoolp,
492 PK11PasswordFunc pwfn, void *pwfn_arg,
493 NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
494 SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
495 {
496 NSSCMSEncoderContext *p7ecx;
497 SECStatus rv;
498 NSSCMSContentInfo *cinfo;
499 SECOidTag tag;
501 NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
502 detached_digestalgs, detached_digests);
504 p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
505 if (p7ecx == NULL) {
506 PORT_SetError(SEC_ERROR_NO_MEMORY);
507 return NULL;
508 }
510 p7ecx->cmsg = cmsg;
511 p7ecx->output.outputfn = outputfn;
512 p7ecx->output.outputarg = outputarg;
513 p7ecx->output.dest = dest;
514 p7ecx->output.destpoolp = destpoolp;
515 p7ecx->type = SEC_OID_UNKNOWN;
517 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
519 tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
520 switch (tag) {
521 case SEC_OID_PKCS7_SIGNED_DATA:
522 rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
523 break;
524 case SEC_OID_PKCS7_ENVELOPED_DATA:
525 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
526 break;
527 case SEC_OID_PKCS7_DIGESTED_DATA:
528 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
529 break;
530 case SEC_OID_PKCS7_ENCRYPTED_DATA:
531 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
532 break;
533 default:
534 if (NSS_CMSType_IsWrapper(tag)) {
535 rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag,
536 p7ecx->content.genericData);
537 } else {
538 rv = SECFailure;
539 }
540 break;
541 }
542 if (rv != SECSuccess) {
543 PORT_Free(p7ecx);
544 return NULL;
545 }
547 /* Initialize the BER encoder.
548 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
549 p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
550 nss_cms_encoder_out, &(p7ecx->output));
551 if (p7ecx->ecx == NULL) {
552 PORT_Free (p7ecx);
553 return NULL;
554 }
555 p7ecx->ecxupdated = PR_FALSE;
557 /*
558 * Indicate that we are streaming. We will be streaming until we
559 * get past the contents bytes.
560 */
561 if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream)
562 SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
564 /*
565 * The notify function will watch for the contents field.
566 */
567 SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
569 /* this will kick off the encoding process & encode everything up to the content bytes,
570 * at which point the notify function sets streaming mode (and possibly creates
571 * a child encoder). */
572 p7ecx->ecxupdated = PR_TRUE;
573 if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
574 PORT_Free (p7ecx);
575 return NULL;
576 }
578 return p7ecx;
579 }
581 /*
582 * NSS_CMSEncoder_Update - take content data delivery from the user
583 *
584 * "p7ecx" - encoder context
585 * "data" - content data
586 * "len" - length of content data
587 *
588 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
589 * then hand the data to the work_data fn
590 */
591 SECStatus
592 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
593 {
594 SECStatus rv;
595 NSSCMSContentInfo *cinfo;
596 SECOidTag childtype;
598 if (p7ecx->error)
599 return SECFailure;
601 /* hand data to the innermost decoder */
602 if (p7ecx->childp7ecx) {
603 /* tell the child to start encoding, up to its first data byte, if it
604 * hasn't started yet */
605 if (!p7ecx->childp7ecx->ecxupdated) {
606 p7ecx->childp7ecx->ecxupdated = PR_TRUE;
607 if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess)
608 return SECFailure;
609 }
610 /* recursion here */
611 rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
612 } else {
613 /* we are at innermost decoder */
614 /* find out about our inner content type - must be data */
615 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
616 if (!cinfo) {
617 /* The original programmer didn't expect this to happen */
618 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
619 return SECFailure;
620 }
622 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
623 if (!NSS_CMSType_IsData(childtype))
624 return SECFailure;
625 /* and we must not have preset data */
626 if (cinfo->content.data != NULL)
627 return SECFailure;
629 /* hand it the data so it can encode it (let DER trickle up the chain) */
630 rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
631 }
632 return rv;
633 }
635 /*
636 * NSS_CMSEncoder_Cancel - stop all encoding
637 *
638 * we need to walk down the chain of encoders and the finish them from the innermost out
639 */
640 SECStatus
641 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
642 {
643 SECStatus rv = SECFailure;
645 /* XXX do this right! */
647 /*
648 * Finish any inner decoders before us so that all the encoded data is flushed
649 * This basically finishes all the decoders from the innermost to the outermost.
650 * Finishing an inner decoder may result in data being updated to the outer decoder
651 * while we are already in NSS_CMSEncoder_Finish, but that's allright.
652 */
653 if (p7ecx->childp7ecx) {
654 rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
655 /* remember rv for now */
656 }
658 /*
659 * On the way back up, there will be no more data (if we had an
660 * inner encoder, it is done now!)
661 * Flush out any remaining data and/or finish digests.
662 */
663 rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
664 if (rv != SECSuccess)
665 goto loser;
667 p7ecx->childp7ecx = NULL;
669 /* kick the encoder back into working mode again.
670 * We turn off streaming stuff (which will cause the encoder to continue
671 * encoding happily, now that we have all the data (like digests) ready for it).
672 */
673 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
674 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
676 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
677 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
679 loser:
680 SEC_ASN1EncoderFinish(p7ecx->ecx);
681 PORT_Free (p7ecx);
682 return rv;
683 }
685 /*
686 * NSS_CMSEncoder_Finish - signal the end of data
687 *
688 * we need to walk down the chain of encoders and the finish them from the innermost out
689 */
690 SECStatus
691 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
692 {
693 SECStatus rv = SECFailure;
694 NSSCMSContentInfo *cinfo;
696 /*
697 * Finish any inner decoders before us so that all the encoded data is flushed
698 * This basically finishes all the decoders from the innermost to the outermost.
699 * Finishing an inner decoder may result in data being updated to the outer decoder
700 * while we are already in NSS_CMSEncoder_Finish, but that's allright.
701 */
702 if (p7ecx->childp7ecx) {
703 /* tell the child to start encoding, up to its first data byte, if it
704 * hasn't yet */
705 if (!p7ecx->childp7ecx->ecxupdated) {
706 p7ecx->childp7ecx->ecxupdated = PR_TRUE;
707 rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0);
708 if (rv != SECSuccess) {
709 NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
710 goto loser;
711 }
712 }
713 rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
714 if (rv != SECSuccess)
715 goto loser;
716 }
718 /*
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.
722 */
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 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
730 if (!cinfo) {
731 /* The original programmer didn't expect this to happen */
732 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE;
733 rv = SECFailure;
734 goto loser;
735 }
736 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
737 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
738 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
739 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
741 if (p7ecx->error)
742 rv = SECFailure;
744 loser:
745 SEC_ASN1EncoderFinish(p7ecx->ecx);
746 PORT_Free (p7ecx);
747 return rv;
748 }