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