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