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