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