cmsencode.c (28357B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * CMS encoding. 7 */ 8 9 #include "cmslocal.h" 10 11 #include "cert.h" 12 #include "keyhi.h" 13 #include "secasn1.h" 14 #include "secoid.h" 15 #include "secitem.h" 16 #include "pk11func.h" 17 #include "secerr.h" 18 19 struct nss_cms_encoder_output { 20 NSSCMSContentCallback outputfn; 21 void *outputarg; 22 PLArenaPool *destpoolp; 23 SECItem *dest; 24 }; 25 26 struct NSSCMSEncoderContextStr { 27 SEC_ASN1EncoderContext *ecx; /* ASN.1 encoder context */ 28 PRBool ecxupdated; /* true if data was handed in */ 29 NSSCMSMessage *cmsg; /* pointer to the root message */ 30 SECOidTag type; /* type tag of the current content */ 31 NSSCMSContent content; /* pointer to current content */ 32 struct nss_cms_encoder_output output; /* output function */ 33 int error; /* error code */ 34 NSSCMSEncoderContext *childp7ecx; /* link to child encoder context */ 35 }; 36 37 static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx); 38 static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx); 39 static void nss_cms_encoder_update(void *p7ecx, 40 const char *data, unsigned long len); 41 static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, 42 const unsigned char *data, unsigned long len, 43 PRBool final, PRBool innermost); 44 45 extern const SEC_ASN1Template NSSCMSMessageTemplate[]; 46 47 /* 48 * The little output function that the ASN.1 encoder calls to hand 49 * us bytes which we in turn hand back to our caller (via the callback 50 * they gave us). 51 */ 52 static void 53 nss_cms_encoder_out(void *arg, const char *buf, unsigned long len, 54 int depth, SEC_ASN1EncodingPart data_kind) 55 { 56 struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; 57 unsigned char *dest; 58 unsigned long offset; 59 60 #ifdef CMSDEBUG 61 int i; 62 const char *data_name = "unknown"; 63 64 switch (data_kind) { 65 case SEC_ASN1_Identifier: 66 data_name = "identifier"; 67 break; 68 case SEC_ASN1_Length: 69 data_name = "length"; 70 break; 71 case SEC_ASN1_Contents: 72 data_name = "contents"; 73 break; 74 case SEC_ASN1_EndOfContents: 75 data_name = "end-of-contents"; 76 break; 77 } 78 fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len); 79 for (i = 0; i < len; i++) { 80 fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); 81 } 82 if ((i % 16) != 0) 83 fprintf(stderr, "\n"); 84 #endif 85 86 if (output->outputfn != NULL) 87 /* call output callback with DER data */ 88 output->outputfn(output->outputarg, buf, len); 89 90 if (output->dest != NULL) { 91 /* store DER data in SECItem */ 92 offset = output->dest->len; 93 if (offset == 0) { 94 dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len); 95 } else { 96 dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 97 output->dest->data, 98 output->dest->len, 99 output->dest->len + len); 100 } 101 if (dest == NULL) 102 /* oops */ 103 return; 104 105 output->dest->data = dest; 106 output->dest->len += len; 107 108 /* copy it in */ 109 if (len) { 110 PORT_Memcpy(output->dest->data + offset, buf, len); 111 } 112 } 113 } 114 115 /* 116 * nss_cms_encoder_notify - ASN.1 encoder callback 117 * 118 * this function is called by the ASN.1 encoder before and after the encoding of 119 * every object. here, it is used to keep track of data structures, set up 120 * encryption and/or digesting and possibly set up child encoders. 121 */ 122 static void 123 nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth) 124 { 125 NSSCMSEncoderContext *p7ecx; 126 NSSCMSContentInfo *rootcinfo, *cinfo; 127 PRBool after = !before; 128 SECOidTag childtype; 129 SECItem *item; 130 131 p7ecx = (NSSCMSEncoderContext *)arg; 132 PORT_Assert(p7ecx != NULL); 133 134 rootcinfo = &(p7ecx->cmsg->contentInfo); 135 136 #ifdef CMSDEBUG 137 fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", 138 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 if (NSS_CMSType_IsData(p7ecx->type)) { 146 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 147 if (before && dest == &(cinfo->rawContent)) { 148 /* just set up encoder to grab from user - no encryption or digesting */ 149 if ((item = cinfo->content.data) != NULL) 150 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, 151 item->len, PR_TRUE, PR_TRUE); 152 else 153 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 154 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ 155 } 156 } else if (NSS_CMSType_IsWrapper(p7ecx->type)) { 157 /* when we know what the content is, we encode happily until we reach the inner content */ 158 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 159 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 160 161 if (after && dest == &(cinfo->contentType)) { 162 /* we're right before encoding the data (if we have some or not) */ 163 /* (for encrypted data, we're right before the contentEncAlg which may change */ 164 /* in nss_cms_before_data because of IV calculation when setting up encryption) */ 165 if (nss_cms_before_data(p7ecx) != SECSuccess) 166 p7ecx->error = PORT_GetError(); 167 } 168 if (before && dest == &(cinfo->rawContent)) { 169 if (p7ecx->childp7ecx == NULL) { 170 if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) { 171 /* we are the innermost non-data and we have data - feed it in */ 172 (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, 173 item->len, PR_TRUE, PR_TRUE); 174 } else { 175 /* else we'll have to get data from user */ 176 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 177 } 178 } else { 179 /* if we have a nested encoder, wait for its data */ 180 SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 181 } 182 } 183 if (after && dest == &(cinfo->rawContent)) { 184 if (nss_cms_after_data(p7ecx) != SECSuccess) 185 p7ecx->error = PORT_GetError(); 186 SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ 187 } 188 } else { 189 /* we're still in the root message */ 190 if (after && dest == &(rootcinfo->contentType)) { 191 /* got the content type OID now - so find out the type tag */ 192 p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); 193 /* set up a pointer to our current content */ 194 p7ecx->content = rootcinfo->content; 195 } 196 } 197 } 198 199 /* 200 * nss_cms_before_data - setup the current encoder to receive data 201 */ 202 static SECStatus 203 nss_cms_before_data(NSSCMSEncoderContext *p7ecx) 204 { 205 SECStatus rv; 206 SECOidTag childtype; 207 NSSCMSContentInfo *cinfo; 208 NSSCMSEncoderContext *childp7ecx; 209 const SEC_ASN1Template *template; 210 211 /* call _Encode_BeforeData handlers */ 212 switch (p7ecx->type) { 213 case SEC_OID_PKCS7_SIGNED_DATA: 214 /* we're encoding a signedData, so set up the digests */ 215 rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData); 216 break; 217 case SEC_OID_PKCS7_DIGESTED_DATA: 218 /* we're encoding a digestedData, so set up the digest */ 219 rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData); 220 break; 221 case SEC_OID_PKCS7_ENVELOPED_DATA: 222 rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData); 223 break; 224 case SEC_OID_PKCS7_ENCRYPTED_DATA: 225 rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData); 226 break; 227 default: 228 if (NSS_CMSType_IsWrapper(p7ecx->type)) { 229 rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, 230 p7ecx->content.genericData); 231 } else { 232 rv = SECFailure; 233 } 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 = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 242 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 243 244 if (NSS_CMSType_IsWrapper(childtype)) { 245 /* in these cases, we need to set up a child encoder! */ 246 /* create new encoder context */ 247 childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); 248 if (childp7ecx == NULL) 249 return SECFailure; 250 251 /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder 252 * (which will encrypt and/or digest it) 253 * this needs to route back into our update function 254 * which finds the lowest encoding context & encrypts and computes digests */ 255 childp7ecx->type = childtype; 256 childp7ecx->content = cinfo->content; 257 /* use the non-recursive update function here, of course */ 258 childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update; 259 childp7ecx->output.outputarg = p7ecx; 260 childp7ecx->output.destpoolp = NULL; 261 childp7ecx->output.dest = NULL; 262 childp7ecx->cmsg = p7ecx->cmsg; 263 childp7ecx->ecxupdated = PR_FALSE; 264 childp7ecx->childp7ecx = NULL; 265 266 template = NSS_CMSUtil_GetTemplateByTypeTag(childtype); 267 if (template == NULL) 268 goto loser; /* cannot happen */ 269 270 /* now initialize the data for encoding the first third */ 271 switch (childp7ecx->type) { 272 case SEC_OID_PKCS7_SIGNED_DATA: 273 rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); 274 break; 275 case SEC_OID_PKCS7_ENVELOPED_DATA: 276 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); 277 break; 278 case SEC_OID_PKCS7_DIGESTED_DATA: 279 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); 280 break; 281 case SEC_OID_PKCS7_ENCRYPTED_DATA: 282 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); 283 break; 284 default: 285 rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, 286 cinfo->content.genericData); 287 break; 288 } 289 if (rv != SECSuccess) 290 goto loser; 291 292 /* 293 * Initialize the BER encoder. 294 */ 295 childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, 296 nss_cms_encoder_out, &(childp7ecx->output)); 297 if (childp7ecx->ecx == NULL) 298 goto loser; 299 300 /* 301 * Indicate that we are streaming. We will be streaming until we 302 * get past the contents bytes. 303 */ 304 if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) 305 SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); 306 307 /* 308 * The notify function will watch for the contents field. 309 */ 310 p7ecx->childp7ecx = childp7ecx; 311 SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, 312 childp7ecx); 313 314 /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ 315 /* encoding process - we'll do that from the update function instead */ 316 /* otherwise we'd be encoding data from a call of the notify function of the */ 317 /* parent encoder (which would not work) */ 318 319 } else if (NSS_CMSType_IsData(childtype)) { 320 p7ecx->childp7ecx = NULL; 321 } else { 322 /* we do not know this type */ 323 p7ecx->error = SEC_ERROR_BAD_DER; 324 } 325 326 return SECSuccess; 327 328 loser: 329 if (childp7ecx) { 330 if (childp7ecx->ecx) 331 SEC_ASN1EncoderFinish(childp7ecx->ecx); 332 PORT_Free(childp7ecx); 333 p7ecx->childp7ecx = NULL; 334 } 335 return SECFailure; 336 } 337 338 static SECStatus 339 nss_cms_after_data(NSSCMSEncoderContext *p7ecx) 340 { 341 SECStatus rv = SECFailure; 342 343 switch (p7ecx->type) { 344 case SEC_OID_PKCS7_SIGNED_DATA: 345 /* this will finish the digests and sign */ 346 rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData); 347 break; 348 case SEC_OID_PKCS7_ENVELOPED_DATA: 349 rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData); 350 break; 351 case SEC_OID_PKCS7_DIGESTED_DATA: 352 rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData); 353 break; 354 case SEC_OID_PKCS7_ENCRYPTED_DATA: 355 rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData); 356 break; 357 default: 358 if (NSS_CMSType_IsWrapper(p7ecx->type)) { 359 rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, 360 p7ecx->content.genericData); 361 } else { 362 rv = SECFailure; 363 } 364 break; 365 } 366 return rv; 367 } 368 369 /* 370 * nss_cms_encoder_work_data - process incoming data 371 * 372 * (from the user or the next encoding layer) 373 * Here, we need to digest and/or encrypt, then pass it on 374 */ 375 static SECStatus 376 nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, 377 const unsigned char *data, unsigned long len, 378 PRBool final, PRBool innermost) 379 { 380 unsigned char *buf = NULL; 381 SECStatus rv; 382 NSSCMSContentInfo *cinfo; 383 384 rv = SECSuccess; /* may as well be optimistic */ 385 386 /* 387 * We should really have data to process, or we should be trying 388 * to finish/flush the last block. (This is an overly paranoid 389 * check since all callers are in this file and simple inspection 390 * proves they do it right. But it could find a bug in future 391 * modifications/development, that is why it is here.) 392 */ 393 PORT_Assert((data != NULL && len) || final); 394 395 /* we got data (either from the caller, or from a lower level encoder) */ 396 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 397 if (!cinfo) { 398 /* The original programmer didn't expect this to happen */ 399 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 400 return SECFailure; 401 } 402 403 /* Update the running digest. */ 404 if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL) 405 NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); 406 407 /* Encrypt this chunk. */ 408 if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { 409 unsigned int inlen; /* length of data being encrypted */ 410 unsigned int outlen; /* length of encrypted data */ 411 unsigned int buflen; /* length available for encrypted data */ 412 413 inlen = len; 414 buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, 415 inlen, final); 416 if (buflen == 0) { 417 /* 418 * No output is expected, but the input data may be buffered 419 * so we still have to call Encrypt. 420 */ 421 rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0, 422 data, inlen, final); 423 if (final) { 424 len = 0; 425 goto done; 426 } 427 return rv; 428 } 429 430 if (dest != NULL) 431 buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); 432 else 433 buf = (unsigned char *)PORT_Alloc(buflen); 434 435 if (buf == NULL) { 436 rv = SECFailure; 437 } else { 438 rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, 439 &outlen, buflen, 440 data, inlen, final); 441 data = buf; 442 len = outlen; 443 } 444 if (rv != SECSuccess) 445 /* encryption or malloc failed? */ 446 return rv; 447 } 448 449 /* 450 * at this point (data,len) has everything we'd like to give to the CURRENT encoder 451 * (which will encode it, then hand it back to the user or the parent encoder) 452 * We don't encode the data if we're innermost and we're told not to include the data 453 */ 454 if (p7ecx->ecx != NULL && len && 455 (!innermost || cinfo->rawContent != cinfo->content.pointer)) 456 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); 457 458 done: 459 460 if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { 461 if (dest != NULL) { 462 dest->data = buf; 463 dest->len = len; 464 } else if (buf != NULL) { 465 PORT_Free(buf); 466 } 467 } 468 return rv; 469 } 470 471 /* 472 * nss_cms_encoder_update - deliver encoded data to the next higher level 473 * 474 * no recursion here because we REALLY want to end up at the next higher encoder! 475 */ 476 static void 477 nss_cms_encoder_update(void *p7ecx, const char *data, unsigned long len) 478 { 479 /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ 480 (void)nss_cms_encoder_work_data((NSSCMSEncoderContext *)p7ecx, NULL, (const unsigned char *)data, 481 len, PR_FALSE, PR_FALSE); 482 } 483 484 /* 485 * NSS_CMSEncoder_Start - set up encoding of a CMS message 486 * 487 * "cmsg" - message to encode 488 * "outputfn", "outputarg" - callback function for delivery of DER-encoded output 489 * will not be called if NULL. 490 * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output 491 * "destpoolp" - pool to allocate DER-encoded output in 492 * "pwfn", pwfn_arg" - callback function for getting token password 493 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData 494 * "detached_digestalgs", "detached_digests" - digests from detached content 495 */ 496 NSSCMSEncoderContext * 497 NSS_CMSEncoder_Start(NSSCMSMessage *cmsg, 498 NSSCMSContentCallback outputfn, void *outputarg, 499 SECItem *dest, PLArenaPool *destpoolp, 500 PK11PasswordFunc pwfn, void *pwfn_arg, 501 NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, 502 SECAlgorithmID **detached_digestalgs, SECItem **detached_digests) 503 { 504 NSSCMSEncoderContext *p7ecx; 505 SECStatus rv; 506 NSSCMSContentInfo *cinfo; 507 SECOidTag tag; 508 509 NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, 510 detached_digestalgs, detached_digests); 511 512 p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); 513 if (p7ecx == NULL) { 514 PORT_SetError(SEC_ERROR_NO_MEMORY); 515 return NULL; 516 } 517 518 p7ecx->cmsg = cmsg; 519 p7ecx->output.outputfn = outputfn; 520 p7ecx->output.outputarg = outputarg; 521 p7ecx->output.dest = dest; 522 p7ecx->output.destpoolp = destpoolp; 523 p7ecx->type = SEC_OID_UNKNOWN; 524 525 cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 526 527 tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 528 switch (tag) { 529 case SEC_OID_PKCS7_SIGNED_DATA: 530 rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); 531 break; 532 case SEC_OID_PKCS7_ENVELOPED_DATA: 533 rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); 534 break; 535 case SEC_OID_PKCS7_DIGESTED_DATA: 536 rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); 537 break; 538 case SEC_OID_PKCS7_ENCRYPTED_DATA: 539 rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); 540 break; 541 default: 542 if (NSS_CMSType_IsWrapper(tag)) { 543 rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, 544 p7ecx->content.genericData); 545 } else { 546 rv = SECFailure; 547 } 548 break; 549 } 550 if (rv != SECSuccess) { 551 PORT_Free(p7ecx); 552 return NULL; 553 } 554 555 /* Initialize the BER encoder. 556 * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ 557 p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate, 558 nss_cms_encoder_out, &(p7ecx->output)); 559 if (p7ecx->ecx == NULL) { 560 PORT_Free(p7ecx); 561 return NULL; 562 } 563 p7ecx->ecxupdated = PR_FALSE; 564 565 /* 566 * Indicate that we are streaming. We will be streaming until we 567 * get past the contents bytes. 568 */ 569 if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) 570 SEC_ASN1EncoderSetStreaming(p7ecx->ecx); 571 572 /* 573 * The notify function will watch for the contents field. 574 */ 575 SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); 576 577 /* this will kick off the encoding process & encode everything up to the content bytes, 578 * at which point the notify function sets streaming mode (and possibly creates 579 * a child encoder). */ 580 p7ecx->ecxupdated = PR_TRUE; 581 if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { 582 PORT_Free(p7ecx); 583 return NULL; 584 } 585 586 return p7ecx; 587 } 588 589 /* 590 * NSS_CMSEncoder_Update - take content data delivery from the user 591 * 592 * "p7ecx" - encoder context 593 * "data" - content data 594 * "len" - length of content data 595 * 596 * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), 597 * then hand the data to the work_data fn 598 */ 599 SECStatus 600 NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) 601 { 602 SECStatus rv; 603 NSSCMSContentInfo *cinfo; 604 SECOidTag childtype; 605 606 if (p7ecx->error) 607 return SECFailure; 608 609 /* hand data to the innermost decoder */ 610 if (p7ecx->childp7ecx) { 611 /* tell the child to start encoding, up to its first data byte, if it 612 * hasn't started yet */ 613 if (!p7ecx->childp7ecx->ecxupdated) { 614 p7ecx->childp7ecx->ecxupdated = PR_TRUE; 615 if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess) 616 return SECFailure; 617 } 618 /* recursion here */ 619 rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len); 620 } else { 621 /* we are at innermost decoder */ 622 /* find out about our inner content type - must be data */ 623 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 624 if (!cinfo) { 625 /* The original programmer didn't expect this to happen */ 626 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 627 return SECFailure; 628 } 629 630 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 631 if (!NSS_CMSType_IsData(childtype)) 632 return SECFailure; 633 /* and we must not have preset data */ 634 if (cinfo->content.data != NULL) 635 return SECFailure; 636 637 /* hand it the data so it can encode it (let DER trickle up the chain) */ 638 rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, 639 len, PR_FALSE, PR_TRUE); 640 } 641 return rv; 642 } 643 644 /* 645 * NSS_CMSEncoder_Cancel - stop all encoding 646 * 647 * we need to walk down the chain of encoders and the finish them from the innermost out 648 */ 649 SECStatus 650 NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx) 651 { 652 SECStatus rv = SECFailure; 653 654 /* XXX do this right! */ 655 656 /* 657 * Finish any inner decoders before us so that all the encoded data is flushed 658 * This basically finishes all the decoders from the innermost to the outermost. 659 * Finishing an inner decoder may result in data being updated to the outer decoder 660 * while we are already in NSS_CMSEncoder_Finish, but that's allright. 661 */ 662 if (p7ecx->childp7ecx) { 663 rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 664 /* remember rv for now */ 665 #ifdef CMSDEBUG 666 if (rv != SECSuccess) { 667 fprintf(stderr, "Fail to cancel inner encoder\n"); 668 } 669 #endif 670 } 671 672 /* 673 * On the way back up, there will be no more data (if we had an 674 * inner encoder, it is done now!) 675 * Flush out any remaining data and/or finish digests. 676 */ 677 rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); 678 if (rv != SECSuccess) 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 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); 692 693 loser: 694 SEC_ASN1EncoderFinish(p7ecx->ecx); 695 PORT_Free(p7ecx); 696 return rv; 697 } 698 699 /* 700 * NSS_CMSEncoder_Finish - signal the end of data 701 * 702 * we need to walk down the chain of encoders and the finish them from the innermost out 703 */ 704 SECStatus 705 NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx) 706 { 707 SECStatus rv = SECFailure; 708 NSSCMSContentInfo *cinfo; 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 NSS_CMSEncoder_Finish, but that's allright. 715 */ 716 if (p7ecx->childp7ecx) { 717 /* tell the child to start encoding, up to its first data byte, if it 718 * hasn't yet */ 719 if (!p7ecx->childp7ecx->ecxupdated) { 720 p7ecx->childp7ecx->ecxupdated = PR_TRUE; 721 rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0); 722 if (rv != SECSuccess) { 723 NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 724 goto loser; 725 } 726 } 727 rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 728 if (rv != SECSuccess) 729 goto loser; 730 } 731 732 /* 733 * On the way back up, there will be no more data (if we had an 734 * inner encoder, it is done now!) 735 * Flush out any remaining data and/or finish digests. 736 */ 737 rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); 738 if (rv != SECSuccess) 739 goto loser; 740 741 p7ecx->childp7ecx = NULL; 742 743 cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 744 if (!cinfo) { 745 /* The original programmer didn't expect this to happen */ 746 p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 747 rv = SECFailure; 748 goto loser; 749 } 750 SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); 751 SEC_ASN1EncoderClearStreaming(p7ecx->ecx); 752 /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 753 rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); 754 755 if (p7ecx->error) 756 rv = SECFailure; 757 758 loser: 759 SEC_ASN1EncoderFinish(p7ecx->ecx); 760 PORT_Free(p7ecx); 761 return rv; 762 }