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