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