]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmsencode.c
Security-57031.30.12.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmsencode.c
1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above. If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL. If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34 /*
35 * CMS encoding.
36 */
37
38 #include <Security/SecCmsEncoder.h>
39 #include <Security/SecCmsContentInfo.h>
40 #include <Security/SecCmsDigestContext.h>
41 #include <Security/SecCmsMessage.h>
42
43 #include "cmslocal.h"
44
45 #include "secoid.h"
46 #include "SecAsn1Item.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <security_asn1/secport.h>
51
52 #include <Security/SecBase.h>
53
54 #include <limits.h>
55
56 struct nss_cms_encoder_output {
57 SecCmsContentCallback outputfn;
58 void *outputarg;
59 CFMutableDataRef berData;
60 };
61
62 struct SecCmsEncoderStr {
63 SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
64 Boolean ecxupdated; /* true if data was handed in */
65 SecCmsMessageRef cmsg; /* pointer to the root message */
66 SECOidTag type; /* type tag of the current content */
67 SecCmsContent content; /* pointer to current content */
68 struct nss_cms_encoder_output output; /* output function */
69 int error; /* error code */
70 SecCmsEncoderRef childp7ecx; /* link to child encoder context */
71 };
72
73 static OSStatus nss_cms_before_data(SecCmsEncoderRef p7ecx);
74 static OSStatus nss_cms_after_data(SecCmsEncoderRef p7ecx);
75 static void nss_cms_encoder_update(void *arg, const char *data, size_t len);
76 static OSStatus nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, SecAsn1Item * dest,
77 const unsigned char *data, size_t len,
78 Boolean final, Boolean innermost);
79
80 extern const SecAsn1Template SecCmsMessageTemplate[];
81
82 /*
83 * The little output function that the ASN.1 encoder calls to hand
84 * us bytes which we in turn hand back to our caller (via the callback
85 * they gave us).
86 */
87 static void
88 nss_cms_encoder_out(void *arg, const char *buf, size_t len,
89 int depth, SEC_ASN1EncodingPart data_kind)
90 {
91 struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
92
93 #ifdef CMSDEBUG
94 size_t i;
95
96 fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len);
97 for (i=0; i < len; i++) {
98 fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
99 }
100 if ((i % 16) != 0)
101 fprintf(stderr, "\n");
102 #endif
103
104 if (output->outputfn != NULL)
105 /* call output callback with DER data */
106 output->outputfn(output->outputarg, buf, len);
107
108 if (output->berData != NULL) {
109 /* store DER data in output->dest */
110 CFDataAppendBytes(output->berData, (const UInt8 *)buf, len);
111 }
112 }
113
114 /*
115 * nss_cms_encoder_notify - ASN.1 encoder callback
116 *
117 * this function is called by the ASN.1 encoder before and after the encoding of
118 * every object. here, it is used to keep track of data structures, set up
119 * encryption and/or digesting and possibly set up child encoders.
120 */
121 static void
122 nss_cms_encoder_notify(void *arg, Boolean before, void *dest, int depth)
123 {
124 SecCmsEncoderRef p7ecx;
125 SecCmsContentInfoRef rootcinfo, cinfo;
126 Boolean after = !before;
127 PLArenaPool *poolp;
128 SECOidTag childtype;
129 SecAsn1Item * item;
130
131 p7ecx = (SecCmsEncoderRef)arg;
132 PORT_Assert(p7ecx != NULL);
133
134 rootcinfo = &(p7ecx->cmsg->contentInfo);
135 poolp = p7ecx->cmsg->poolp;
136
137 #ifdef CMSDEBUG
138 fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", 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 switch (p7ecx->type) {
146 default:
147 case SEC_OID_UNKNOWN:
148 /* we're still in the root message */
149 if (after && dest == &(rootcinfo->contentType)) {
150 /* got the content type OID now - so find out the type tag */
151 p7ecx->type = SecCmsContentInfoGetContentTypeTag(rootcinfo);
152 /* set up a pointer to our current content */
153 p7ecx->content = rootcinfo->content;
154 }
155 break;
156
157 case SEC_OID_PKCS7_DATA:
158 if (before && dest == &(rootcinfo->rawContent)) {
159 /* just set up encoder to grab from user - no encryption or digesting */
160 if ((item = rootcinfo->content.data) != NULL)
161 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE);
162 else
163 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
164 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
165 }
166 break;
167
168 case SEC_OID_PKCS7_SIGNED_DATA:
169 case SEC_OID_PKCS7_ENVELOPED_DATA:
170 case SEC_OID_PKCS7_DIGESTED_DATA:
171 case SEC_OID_PKCS7_ENCRYPTED_DATA:
172
173 /* when we know what the content is, we encode happily until we reach the inner content */
174 cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type);
175 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
176
177 if (after && dest == &(cinfo->contentType)) {
178 /* we're right before encoding the data (if we have some or not) */
179 /* (for encrypted data, we're right before the contentEncAlg which may change */
180 /* in nss_cms_before_data because of IV calculation when setting up encryption) */
181 if (nss_cms_before_data(p7ecx) != SECSuccess)
182 p7ecx->error = PORT_GetError();
183 }
184 if (before && dest == &(cinfo->rawContent)) {
185 if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
186 /* we have data - feed it in */
187 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE);
188 else
189 /* else try to get it from user */
190 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
191 }
192 if (after && dest == &(cinfo->rawContent)) {
193 if (nss_cms_after_data(p7ecx) != SECSuccess)
194 p7ecx->error = PORT_GetError();
195 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
196 }
197 break;
198 }
199 }
200
201 /*
202 * nss_cms_before_data - setup the current encoder to receive data
203 */
204 static OSStatus
205 nss_cms_before_data(SecCmsEncoderRef p7ecx)
206 {
207 OSStatus rv;
208 SECOidTag childtype;
209 SecCmsContentInfoRef cinfo;
210 PLArenaPool *poolp;
211 SecCmsEncoderRef childp7ecx;
212 const SecAsn1Template *template;
213
214 poolp = p7ecx->cmsg->poolp;
215
216 /* call _Encode_BeforeData handlers */
217 switch (p7ecx->type) {
218 case SEC_OID_PKCS7_SIGNED_DATA:
219 /* we're encoding a signedData, so set up the digests */
220 rv = SecCmsSignedDataEncodeBeforeData(p7ecx->content.signedData);
221 break;
222 case SEC_OID_PKCS7_DIGESTED_DATA:
223 /* we're encoding a digestedData, so set up the digest */
224 rv = SecCmsDigestedDataEncodeBeforeData(p7ecx->content.digestedData);
225 break;
226 case SEC_OID_PKCS7_ENVELOPED_DATA:
227 rv = SecCmsEnvelopedDataEncodeBeforeData(p7ecx->content.envelopedData);
228 break;
229 case SEC_OID_PKCS7_ENCRYPTED_DATA:
230 rv = SecCmsEncryptedDataEncodeBeforeData(p7ecx->content.encryptedData);
231 break;
232 default:
233 rv = SECFailure;
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 = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type);
242 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
243
244 switch (childtype) {
245 case SEC_OID_PKCS7_SIGNED_DATA:
246 case SEC_OID_PKCS7_ENVELOPED_DATA:
247 case SEC_OID_PKCS7_ENCRYPTED_DATA:
248 case SEC_OID_PKCS7_DIGESTED_DATA:
249 #if 0
250 case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */
251 #endif
252 /* in these cases, we need to set up a child encoder! */
253 /* create new encoder context */
254 childp7ecx = PORT_ZAlloc(sizeof(struct SecCmsEncoderStr));
255 if (childp7ecx == NULL)
256 return SECFailure;
257
258 /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
259 * (which will encrypt and/or digest it)
260 * this needs to route back into our update function
261 * which finds the lowest encoding context & encrypts and computes digests */
262 childp7ecx->type = childtype;
263 childp7ecx->content = cinfo->content;
264 /* use the non-recursive update function here, of course */
265 childp7ecx->output.outputfn = nss_cms_encoder_update;
266 childp7ecx->output.outputarg = p7ecx;
267 childp7ecx->output.berData = NULL;
268 childp7ecx->cmsg = p7ecx->cmsg;
269
270 template = SecCmsUtilGetTemplateByTypeTag(childtype);
271 if (template == NULL)
272 goto loser; /* cannot happen */
273
274 /* now initialize the data for encoding the first third */
275 switch (childp7ecx->type) {
276 case SEC_OID_PKCS7_SIGNED_DATA:
277 rv = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData);
278 break;
279 case SEC_OID_PKCS7_ENVELOPED_DATA:
280 rv = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData);
281 break;
282 case SEC_OID_PKCS7_DIGESTED_DATA:
283 rv = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData);
284 break;
285 case SEC_OID_PKCS7_ENCRYPTED_DATA:
286 rv = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData);
287 break;
288 case SEC_OID_PKCS7_DATA:
289 rv = SECSuccess;
290 break;
291 default:
292 PORT_Assert(0);
293 break;
294 }
295 if (rv != SECSuccess)
296 goto loser;
297
298 /*
299 * Initialize the BER encoder.
300 */
301 childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
302 nss_cms_encoder_out, &(childp7ecx->output));
303 if (childp7ecx->ecx == NULL)
304 goto loser;
305
306 childp7ecx->ecxupdated = PR_FALSE;
307
308 /*
309 * Indicate that we are streaming. We will be streaming until we
310 * get past the contents bytes.
311 */
312 SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
313
314 /*
315 * The notify function will watch for the contents field.
316 */
317 SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
318
319 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
320 /* encoding process - we'll do that from the update function instead */
321 /* otherwise we'd be encoding data from a call of the notify function of the */
322 /* parent encoder (which would not work) */
323
324 /* this will kick off the encoding process & encode everything up to the content bytes,
325 * at which point the notify function sets streaming mode (and possibly creates
326 * another child encoder). */
327 if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
328 goto loser;
329
330 p7ecx->childp7ecx = childp7ecx;
331 break;
332
333 case SEC_OID_PKCS7_DATA:
334 p7ecx->childp7ecx = NULL;
335 break;
336 default:
337 /* we do not know this type */
338 p7ecx->error = SEC_ERROR_BAD_DER;
339 break;
340 }
341
342 return SECSuccess;
343
344 loser:
345 if (childp7ecx) {
346 if (childp7ecx->ecx)
347 SEC_ASN1EncoderFinish(childp7ecx->ecx);
348 PORT_Free(childp7ecx);
349 }
350 return SECFailure;
351 }
352
353 static OSStatus
354 nss_cms_after_data(SecCmsEncoderRef p7ecx)
355 {
356 OSStatus rv = SECFailure;
357
358 switch (p7ecx->type) {
359 case SEC_OID_PKCS7_SIGNED_DATA:
360 /* this will finish the digests and sign */
361 rv = SecCmsSignedDataEncodeAfterData(p7ecx->content.signedData);
362 break;
363 case SEC_OID_PKCS7_ENVELOPED_DATA:
364 rv = SecCmsEnvelopedDataEncodeAfterData(p7ecx->content.envelopedData);
365 break;
366 case SEC_OID_PKCS7_DIGESTED_DATA:
367 rv = SecCmsDigestedDataEncodeAfterData(p7ecx->content.digestedData);
368 break;
369 case SEC_OID_PKCS7_ENCRYPTED_DATA:
370 rv = SecCmsEncryptedDataEncodeAfterData(p7ecx->content.encryptedData);
371 break;
372 case SEC_OID_PKCS7_DATA:
373 /* do nothing */
374 break;
375 default:
376 rv = SECFailure;
377 break;
378 }
379 return rv;
380 }
381
382 /*
383 * nss_cms_encoder_work_data - process incoming data
384 *
385 * (from the user or the next encoding layer)
386 * Here, we need to digest and/or encrypt, then pass it on
387 *
388 */
389 static OSStatus
390 nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, SecAsn1Item * dest,
391 const unsigned char *data, size_t len,
392 Boolean final, Boolean innermost)
393 {
394 unsigned char *buf = NULL;
395 OSStatus rv;
396 SecCmsContentInfoRef cinfo;
397
398 rv = SECSuccess; /* may as well be optimistic */
399
400 /*
401 * We should really have data to process, or we should be trying
402 * to finish/flush the last block. (This is an overly paranoid
403 * check since all callers are in this file and simple inspection
404 * proves they do it right. But it could find a bug in future
405 * modifications/development, that is why it is here.)
406 */
407 PORT_Assert ((data != NULL && len) || final);
408 PORT_Assert (len < UINT_MAX); /* overflow check for later cast */
409
410 /* we got data (either from the caller, or from a lower level encoder) */
411 cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type);
412
413 /* Update the running digest. */
414 if (len && cinfo->digcx != NULL)
415 SecCmsDigestContextUpdate(cinfo->digcx, data, len);
416
417 /* Encrypt this chunk. */
418 if (cinfo->ciphcx != NULL) {
419 unsigned int inlen; /* length of data being encrypted */
420 unsigned int outlen = 0; /* length of encrypted data */
421 unsigned int buflen; /* length available for encrypted data */
422
423 /* 64 bits cast: only an issue if unsigned int is smaller than size_t.
424 Worst case is you will truncate a CMS blob bigger than 4GB when
425 encrypting */
426 inlen = (unsigned int)len;
427
428 buflen = SecCmsCipherContextEncryptLength(cinfo->ciphcx, inlen, final);
429 if (buflen == 0) {
430 /*
431 * No output is expected, but the input data may be buffered
432 * so we still have to call Encrypt.
433 */
434 rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, NULL, NULL, 0,
435 data, inlen, final);
436 if (final) {
437 len = 0;
438 goto done;
439 }
440 return rv;
441 }
442
443 if (dest != NULL)
444 buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
445 else
446 buf = (unsigned char*)PORT_Alloc(buflen);
447
448 if (buf == NULL) {
449 rv = SECFailure;
450 } else {
451 rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, buf, &outlen, buflen,
452 data, inlen, final);
453 data = buf;
454 len = outlen;
455 }
456 if (rv != SECSuccess)
457 /* encryption or malloc failed? */
458 return rv;
459 }
460
461
462 /*
463 * at this point (data,len) has everything we'd like to give to the CURRENT encoder
464 * (which will encode it, then hand it back to the user or the parent encoder)
465 * We don't encode the data if we're innermost and we're told not to include the data
466 */
467 if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
468 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
469
470 done:
471
472 if (cinfo->ciphcx != NULL) {
473 if (dest != NULL) {
474 dest->Data = buf;
475 dest->Length = len;
476 } else if (buf != NULL) {
477 PORT_Free (buf);
478 }
479 }
480 return rv;
481 }
482
483 /*
484 * nss_cms_encoder_update - deliver encoded data to the next higher level
485 *
486 * no recursion here because we REALLY want to end up at the next higher encoder!
487 */
488 static void
489 nss_cms_encoder_update(void *arg, const char *data, size_t len)
490 {
491 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
492 SecCmsEncoderRef p7ecx = (SecCmsEncoderRef)arg;
493
494 (void)nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
495 }
496
497 /*
498 * SecCmsEncoderCreate - set up encoding of a CMS message
499 *
500 * "cmsg" - message to encode
501 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
502 * will not be called if NULL.
503 * "dest" - if non-NULL, pointer to SecAsn1Item that will hold the DER-encoded output
504 * "destpoolp" - pool to allocate DER-encoded output in
505 * "pwfn", pwfn_arg" - callback function for getting token password
506 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
507 * "detached_digestalgs", "detached_digests" - digests from detached content
508 */
509 OSStatus
510 SecCmsEncoderCreate(SecCmsMessageRef cmsg,
511 SecCmsContentCallback outputfn, void *outputarg,
512 CFMutableDataRef outBer,
513 PK11PasswordFunc pwfn, void *pwfn_arg,
514 SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
515 SecCmsEncoderRef *outEncoder)
516 {
517 SecCmsEncoderRef p7ecx;
518 OSStatus result;
519 SecCmsContentInfoRef cinfo;
520
521 SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg);
522
523 p7ecx = (SecCmsEncoderRef)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr));
524 if (p7ecx == NULL) {
525 result = errSecAllocate;
526 goto loser;
527 }
528
529 p7ecx->cmsg = cmsg;
530 p7ecx->output.outputfn = outputfn;
531 p7ecx->output.outputarg = outputarg;
532 p7ecx->output.berData = outBer;
533
534 p7ecx->type = SEC_OID_UNKNOWN;
535
536 cinfo = SecCmsMessageGetContentInfo(cmsg);
537
538 switch (SecCmsContentInfoGetContentTypeTag(cinfo)) {
539 case SEC_OID_PKCS7_SIGNED_DATA:
540 result = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData);
541 break;
542 case SEC_OID_PKCS7_ENVELOPED_DATA:
543 result = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData);
544 break;
545 case SEC_OID_PKCS7_DIGESTED_DATA:
546 result = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData);
547 break;
548 case SEC_OID_PKCS7_ENCRYPTED_DATA:
549 result = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData);
550 break;
551 default:
552 /* @@@ We need a better error for unsupported message types. */
553 result = errSecParam;
554 break;
555 }
556 if (result)
557 goto loser;
558
559 /* Initialize the BER encoder.
560 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
561 p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, SecCmsMessageTemplate,
562 nss_cms_encoder_out, &(p7ecx->output));
563 if (p7ecx->ecx == NULL) {
564 result = PORT_GetError();
565 PORT_Free (p7ecx);
566 goto loser;
567 }
568 p7ecx->ecxupdated = PR_FALSE;
569
570 /*
571 * Indicate that we are streaming. We will be streaming until we
572 * get past the contents bytes.
573 */
574 SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
575
576 /*
577 * The notify function will watch for the contents field.
578 */
579 SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
580
581 /* this will kick off the encoding process & encode everything up to the content bytes,
582 * at which point the notify function sets streaming mode (and possibly creates
583 * a child encoder). */
584 if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
585 result = PORT_GetError();
586 PORT_Free (p7ecx);
587 goto loser;
588 }
589
590 *outEncoder = p7ecx;
591 loser:
592 return result;
593 }
594
595 /*
596 * SecCmsEncoderUpdate - take content data delivery from the user
597 *
598 * "p7ecx" - encoder context
599 * "data" - content data
600 * "len" - length of content data
601 *
602 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
603 * then hand the data to the work_data fn
604 */
605 OSStatus
606 SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx, const void *data, CFIndex len)
607 {
608 OSStatus result;
609 SecCmsContentInfoRef cinfo;
610 SECOidTag childtype;
611
612 if (p7ecx->error)
613 return p7ecx->error;
614
615 /* hand data to the innermost decoder */
616 if (p7ecx->childp7ecx) {
617 /* recursion here */
618 result = SecCmsEncoderUpdate(p7ecx->childp7ecx, data, len);
619 } else {
620 /* we are at innermost decoder */
621 /* find out about our inner content type - must be data */
622 cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type);
623 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
624 if (childtype != SEC_OID_PKCS7_DATA)
625 return errSecParam; /* @@@ Maybe come up with a better error? */
626 /* and we must not have preset data */
627 if (cinfo->content.data != NULL)
628 return errSecParam; /* @@@ Maybe come up with a better error? */
629
630 /* hand it the data so it can encode it (let DER trickle up the chain) */
631 result = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
632 if (result)
633 result = PORT_GetError();
634 }
635 return result;
636 }
637
638 /*
639 * SecCmsEncoderDestroy - stop all encoding
640 *
641 * we need to walk down the chain of encoders and the finish them from the innermost out
642 */
643 void
644 SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx)
645 {
646 /* XXX do this right! */
647
648 /*
649 * Finish any inner decoders before us so that all the encoded data is flushed
650 * This basically finishes all the decoders from the innermost to the outermost.
651 * Finishing an inner decoder may result in data being updated to the outer decoder
652 * while we are already in SecCmsEncoderFinish, but that's allright.
653 */
654 if (p7ecx->childp7ecx)
655 SecCmsEncoderDestroy(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
656
657 /*
658 * On the way back up, there will be no more data (if we had an
659 * inner encoder, it is done now!)
660 * Flush out any remaining data and/or finish digests.
661 */
662 if (nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)))
663 goto loser;
664
665 p7ecx->childp7ecx = NULL;
666
667 /* kick the encoder back into working mode again.
668 * We turn off streaming stuff (which will cause the encoder to continue
669 * encoding happily, now that we have all the data (like digests) ready for it).
670 */
671 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
672 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
673
674 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
675 SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
676
677 loser:
678 SEC_ASN1EncoderFinish(p7ecx->ecx);
679 PORT_Free (p7ecx);
680 }
681
682 /*
683 * SecCmsEncoderFinish - signal the end of data
684 *
685 * we need to walk down the chain of encoders and the finish them from the innermost out
686 */
687 OSStatus
688 SecCmsEncoderFinish(SecCmsEncoderRef p7ecx)
689 {
690 OSStatus result;
691 SecCmsContentInfoRef cinfo;
692 SECOidTag childtype;
693
694 /*
695 * Finish any inner decoders before us so that all the encoded data is flushed
696 * This basically finishes all the decoders from the innermost to the outermost.
697 * Finishing an inner decoder may result in data being updated to the outer decoder
698 * while we are already in SecCmsEncoderFinish, but that's allright.
699 */
700 if (p7ecx->childp7ecx) {
701 result = SecCmsEncoderFinish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
702 if (result)
703 goto loser;
704 }
705
706 /*
707 * On the way back up, there will be no more data (if we had an
708 * inner encoder, it is done now!)
709 * Flush out any remaining data and/or finish digests.
710 */
711 result = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
712 if (result) {
713 result = PORT_GetError();
714 goto loser;
715 }
716
717 p7ecx->childp7ecx = NULL;
718
719 /* find out about our inner content type - must be data */
720 cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type);
721 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
722 if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
723 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
724 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
725 result = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
726 if (result)
727 result = PORT_GetError();
728 }
729
730 SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
731
732 if (p7ecx->error && !result)
733 result = p7ecx->error;
734
735 loser:
736 SEC_ASN1EncoderFinish(p7ecx->ecx);
737 PORT_Free (p7ecx);
738 return result;
739 }
740
741 OSStatus
742 SecCmsMessageEncode(SecCmsMessageRef cmsg, const SecAsn1Item *input,
743 CFMutableDataRef outBer)
744 {
745 SecCmsEncoderRef encoder = NULL;
746 OSStatus result;
747
748 if (!cmsg || !outBer) {
749 result = errSecParam;
750 goto loser;
751 }
752
753 result = SecCmsEncoderCreate(cmsg, 0, 0, outBer, 0, 0, 0, 0, &encoder);
754 if (result)
755 goto loser;
756
757 if (input) {
758 result = SecCmsEncoderUpdate(encoder, input->Data, input->Length);
759 if (result) {
760 SecCmsEncoderDestroy(encoder);
761 goto loser;
762 }
763 }
764 result = SecCmsEncoderFinish(encoder);
765
766 loser:
767 return result;
768 }