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