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 52struct nss_cms_encoder_output { 53 SecCmsContentCallback outputfn; 54 void *outputarg; 55 PLArenaPool *destpoolp; 56 CSSM_DATA_PTR dest; 57}; 58 59struct 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 70static OSStatus nss_cms_before_data(SecCmsEncoderRef p7ecx); 71static OSStatus nss_cms_after_data(SecCmsEncoderRef p7ecx); 72static OSStatus nss_cms_encoder_update(SecCmsEncoderRef p7ecx, const char *data, size_t len); 73static 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 77extern 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 */ 84static void 85nss_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 */ 137static void 138nss_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 */ 222static OSStatus 223nss_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 365loser: 366 if (childp7ecx) { 367 if (childp7ecx->ecx) 368 SEC_ASN1EncoderFinish(childp7ecx->ecx); 369 PORT_Free(childp7ecx); 370 } 371 return SECFailure; 372} 373 374static OSStatus 375nss_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 */ 410static OSStatus 411nss_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 486done: 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 */ 504static OSStatus 505nss_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 */ 523OSStatus 524SecCmsEncoderCreate(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; 607loser: 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 */ 621OSStatus 622SecCmsEncoderUpdate(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 */ 659void 660SecCmsEncoderDestroy(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 693loser: 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 */ 703OSStatus 704SecCmsEncoderFinish(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 752loser: 753 SEC_ASN1EncoderFinish(p7ecx->ecx); 754 PORT_Free (p7ecx); 755 return result; 756} 757 758OSStatus 759SecCmsMessageEncode(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 783loser: 784 return result; 785} 786