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