cmsdecode.c (26724B)
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 decoding. 7 */ 8 9 #include "cmslocal.h" 10 11 #include "cert.h" 12 #include "keyhi.h" 13 #include "secasn1.h" 14 #include "secitem.h" 15 #include "secoid.h" 16 #include "prtime.h" 17 #include "secerr.h" 18 19 struct NSSCMSDecoderContextStr { 20 SEC_ASN1DecoderContext *dcx; /* ASN.1 decoder context */ 21 NSSCMSMessage *cmsg; /* backpointer to the root message */ 22 SECOidTag type; /* type of message */ 23 NSSCMSContent content; /* pointer to message */ 24 NSSCMSDecoderContext *childp7dcx; /* inner CMS decoder context */ 25 PRBool saw_contents; 26 int error; 27 NSSCMSContentCallback cb; 28 void *cb_arg; 29 PRBool first_decoded; 30 PRBool need_indefinite_finish; 31 unsigned int max_asn_len; 32 }; 33 34 struct NSSCMSDecoderDataStr { 35 SECItem data; /* must be first */ 36 unsigned int totalBufferSize; 37 }; 38 39 typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData; 40 41 static void nss_cms_decoder_update_filter(void *arg, const char *data, 42 unsigned long len, int depth, 43 SEC_ASN1EncodingPart data_kind); 44 static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx); 45 static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx); 46 static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx); 47 static void nss_cms_decoder_update(void *p7ecx, const char *data, unsigned long len); 48 static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 49 const unsigned char *data, 50 unsigned long len, 51 PRBool final); 52 static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp); 53 54 extern const SEC_ASN1Template NSSCMSMessageTemplate[]; 55 56 void 57 nss_cms_set_max_asn_length(NSSCMSDecoderContext *p7dcx, unsigned int max_asn_len) 58 { 59 p7dcx->max_asn_len = max_asn_len; 60 if (p7dcx->dcx && max_asn_len) { 61 SEC_ASN1DecoderSetMaximumElementSize(p7dcx->dcx, max_asn_len); 62 } 63 } 64 65 static NSSCMSDecoderData * 66 nss_cms_create_decoder_data(PLArenaPool *poolp) 67 { 68 NSSCMSDecoderData *decoderData = NULL; 69 70 decoderData = (NSSCMSDecoderData *) 71 PORT_ArenaAlloc(poolp, sizeof(NSSCMSDecoderData)); 72 if (!decoderData) { 73 return NULL; 74 } 75 decoderData->data.data = NULL; 76 decoderData->data.len = 0; 77 decoderData->totalBufferSize = 0; 78 return decoderData; 79 } 80 81 /* 82 * nss_cms_decoder_notify - 83 * this is the driver of the decoding process. It gets called by the ASN.1 84 * decoder before and after an object is decoded. 85 * at various points in the decoding process, we intercept to set up and do 86 * further processing. 87 */ 88 static void 89 nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth) 90 { 91 NSSCMSDecoderContext *p7dcx; 92 NSSCMSContentInfo *rootcinfo, *cinfo; 93 PRBool after = !before; 94 95 p7dcx = (NSSCMSDecoderContext *)arg; 96 rootcinfo = &(p7dcx->cmsg->contentInfo); 97 98 /* XXX error handling: need to set p7dcx->error */ 99 100 #ifdef CMSDEBUG 101 fprintf(stderr, "%6.6s, dest = 0x%p, depth = %d\n", before ? "before" : "after", 102 dest, depth); 103 #endif 104 105 /* so what are we working on right now? */ 106 if (p7dcx->type == SEC_OID_UNKNOWN) { 107 /* 108 * right now, we are still decoding the OUTER (root) cinfo 109 * As soon as we know the inner content type, set up the info, 110 * but NO inner decoder or filter. The root decoder handles the first 111 * level children by itself - only for encapsulated contents (which 112 * are encoded as DER inside of an OCTET STRING) we need to set up a 113 * child decoder... 114 */ 115 if (after && dest == &(rootcinfo->contentType)) { 116 p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); 117 p7dcx->content = rootcinfo->content; 118 /* is this ready already ? need to alloc? */ 119 /* XXX yes we need to alloc -- continue here */ 120 } 121 } else if (NSS_CMSType_IsData(p7dcx->type)) { 122 /* this can only happen if the outermost cinfo has DATA in it */ 123 /* otherwise, we handle this type implicitely in the inner decoders */ 124 125 if (before && dest == &(rootcinfo->content)) { 126 /* cause the filter to put the data in the right place... 127 ** We want the ASN.1 decoder to deliver the decoded bytes to us 128 ** from now on 129 */ 130 SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, 131 nss_cms_decoder_update_filter, 132 p7dcx, 133 (PRBool)(p7dcx->cb != NULL)); 134 } else if (after && dest == &(rootcinfo->content.data)) { 135 /* remove the filter */ 136 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); 137 } 138 } else if (NSS_CMSType_IsWrapper(p7dcx->type)) { 139 if (!before || dest != &(rootcinfo->content)) { 140 141 if (p7dcx->content.pointer == NULL) 142 p7dcx->content = rootcinfo->content; 143 144 /* get this data type's inner contentInfo */ 145 cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, 146 p7dcx->type); 147 if (cinfo == NULL) { 148 p7dcx->error = SEC_ERROR_BAD_DATA; 149 return; 150 } 151 if (before && dest == &(cinfo->contentType)) { 152 /* at this point, set up the &%$&$ back pointer */ 153 /* we cannot do it later, because the content itself 154 * is optional! */ 155 switch (p7dcx->type) { 156 case SEC_OID_PKCS7_SIGNED_DATA: 157 p7dcx->content.signedData->cmsg = p7dcx->cmsg; 158 break; 159 case SEC_OID_PKCS7_DIGESTED_DATA: 160 p7dcx->content.digestedData->cmsg = p7dcx->cmsg; 161 break; 162 case SEC_OID_PKCS7_ENVELOPED_DATA: 163 p7dcx->content.envelopedData->cmsg = p7dcx->cmsg; 164 break; 165 case SEC_OID_PKCS7_ENCRYPTED_DATA: 166 p7dcx->content.encryptedData->cmsg = p7dcx->cmsg; 167 break; 168 default: 169 p7dcx->content.genericData->cmsg = p7dcx->cmsg; 170 break; 171 } 172 } 173 174 if (before && dest == &(cinfo->rawContent)) { 175 /* we want the ASN.1 decoder to deliver the decoded bytes to us 176 ** from now on 177 */ 178 SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, 179 nss_cms_decoder_update_filter, 180 p7dcx, (PRBool)(p7dcx->cb != NULL)); 181 182 /* we're right in front of the data */ 183 if (nss_cms_before_data(p7dcx) != SECSuccess) { 184 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); 185 /* stop all processing */ 186 p7dcx->error = PORT_GetError(); 187 } 188 } 189 if (after && dest == &(cinfo->rawContent)) { 190 /* we're right after of the data */ 191 if (nss_cms_after_data(p7dcx) != SECSuccess) 192 p7dcx->error = PORT_GetError(); 193 194 /* we don't need to see the contents anymore */ 195 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); 196 } 197 } 198 } else { 199 /* unsupported or unknown message type - fail gracefully */ 200 p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE; 201 } 202 } 203 204 /* 205 * nss_cms_before_data - set up the current encoder to receive data 206 */ 207 static SECStatus 208 nss_cms_before_data(NSSCMSDecoderContext *p7dcx) 209 { 210 SECStatus rv; 211 SECOidTag childtype; 212 PLArenaPool *poolp; 213 NSSCMSDecoderContext *childp7dcx; 214 NSSCMSContentInfo *cinfo; 215 const SEC_ASN1Template *template; 216 void *mark = NULL; 217 size_t size; 218 219 poolp = p7dcx->cmsg->poolp; 220 221 /* call _Decode_BeforeData handlers */ 222 switch (p7dcx->type) { 223 case SEC_OID_PKCS7_SIGNED_DATA: 224 /* we're decoding a signedData, so set up the digests */ 225 rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData); 226 break; 227 case SEC_OID_PKCS7_DIGESTED_DATA: 228 /* we're encoding a digestedData, so set up the digest */ 229 rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData); 230 break; 231 case SEC_OID_PKCS7_ENVELOPED_DATA: 232 rv = NSS_CMSEnvelopedData_Decode_BeforeData( 233 p7dcx->content.envelopedData); 234 break; 235 case SEC_OID_PKCS7_ENCRYPTED_DATA: 236 rv = NSS_CMSEncryptedData_Decode_BeforeData( 237 p7dcx->content.encryptedData); 238 break; 239 default: 240 rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type, 241 p7dcx->content.genericData); 242 } 243 if (rv != SECSuccess) 244 return SECFailure; 245 246 /* ok, now we have a pointer to cinfo */ 247 /* find out what kind of data is encapsulated */ 248 249 cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); 250 childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 251 252 if (NSS_CMSType_IsData(childtype)) { 253 cinfo->content.pointer = (void *)nss_cms_create_decoder_data(poolp); 254 if (cinfo->content.pointer == NULL) 255 /* set memory error */ 256 return SECFailure; 257 258 p7dcx->childp7dcx = NULL; 259 return SECSuccess; 260 } 261 262 /* set up inner decoder */ 263 264 if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL) 265 return SECFailure; 266 267 childp7dcx = PORT_ArenaZNew(poolp, NSSCMSDecoderContext); 268 if (childp7dcx == NULL) 269 return SECFailure; 270 271 mark = PORT_ArenaMark(poolp); 272 273 /* allocate space for the stuff we're creating */ 274 size = NSS_CMSUtil_GetSizeByTypeTag(childtype); 275 childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size); 276 if (childp7dcx->content.pointer == NULL) 277 goto loser; 278 279 /* give the parent a copy of the pointer so that it doesn't get lost */ 280 cinfo->content.pointer = childp7dcx->content.pointer; 281 282 /* start the child decoder */ 283 childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, 284 template); 285 if (childp7dcx->dcx == NULL) 286 goto loser; 287 288 if (p7dcx->max_asn_len) { 289 nss_cms_set_max_asn_length(childp7dcx, p7dcx->max_asn_len); 290 } 291 292 /* the new decoder needs to notify, too */ 293 SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, 294 childp7dcx); 295 296 /* tell the parent decoder that it needs to feed us the content data */ 297 p7dcx->childp7dcx = childp7dcx; 298 299 childp7dcx->type = childtype; /* our type */ 300 301 childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */ 302 303 /* should the child decoder encounter real data, 304 ** it must give it to the caller 305 */ 306 childp7dcx->cb = p7dcx->cb; 307 childp7dcx->cb_arg = p7dcx->cb_arg; 308 childp7dcx->first_decoded = PR_FALSE; 309 childp7dcx->need_indefinite_finish = PR_FALSE; 310 if (childtype == SEC_OID_PKCS7_SIGNED_DATA) { 311 childp7dcx->first_decoded = PR_TRUE; 312 } 313 314 /* now set up the parent to hand decoded data to the next level decoder */ 315 p7dcx->cb = (NSSCMSContentCallback)nss_cms_decoder_update; 316 p7dcx->cb_arg = childp7dcx; 317 318 PORT_ArenaUnmark(poolp, mark); 319 320 return SECSuccess; 321 322 loser: 323 if (mark) 324 PORT_ArenaRelease(poolp, mark); 325 PORT_Free(childp7dcx); 326 p7dcx->childp7dcx = NULL; 327 return SECFailure; 328 } 329 330 static SECStatus 331 nss_cms_after_data(NSSCMSDecoderContext *p7dcx) 332 { 333 NSSCMSDecoderContext *childp7dcx; 334 SECStatus rv = SECFailure; 335 336 /* Handle last block. This is necessary to flush out the last bytes 337 * of a possibly incomplete block */ 338 nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE); 339 340 /* finish any "inner" decoders - there's no more data coming... */ 341 if (p7dcx->childp7dcx != NULL) { 342 childp7dcx = p7dcx->childp7dcx; 343 if (childp7dcx->dcx != NULL) { 344 /* we started and indefinite sequence somewhere, not complete it */ 345 if (childp7dcx->need_indefinite_finish) { 346 static const char lbuf[2] = { 0, 0 }; 347 NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf)); 348 childp7dcx->need_indefinite_finish = PR_FALSE; 349 } 350 351 rv = nss_cms_after_end(childp7dcx); 352 if (rv != SECSuccess) 353 goto done; 354 } 355 p7dcx->childp7dcx = NULL; 356 } 357 358 switch (p7dcx->type) { 359 case SEC_OID_PKCS7_SIGNED_DATA: 360 /* this will finish the digests and verify */ 361 rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData); 362 break; 363 case SEC_OID_PKCS7_ENVELOPED_DATA: 364 rv = NSS_CMSEnvelopedData_Decode_AfterData( 365 p7dcx->content.envelopedData); 366 break; 367 case SEC_OID_PKCS7_DIGESTED_DATA: 368 rv = NSS_CMSDigestedData_Decode_AfterData( 369 p7dcx->content.digestedData); 370 break; 371 case SEC_OID_PKCS7_ENCRYPTED_DATA: 372 rv = NSS_CMSEncryptedData_Decode_AfterData( 373 p7dcx->content.encryptedData); 374 break; 375 case SEC_OID_PKCS7_DATA: 376 /* do nothing */ 377 break; 378 default: 379 rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type, 380 p7dcx->content.genericData); 381 break; 382 } 383 done: 384 return rv; 385 } 386 387 static SECStatus 388 nss_cms_after_end(NSSCMSDecoderContext *p7dcx) 389 { 390 SECStatus rv = SECSuccess, rv1 = SECSuccess, rv2 = SECSuccess; 391 392 /* Finish any child decoders */ 393 if (p7dcx->childp7dcx) { 394 rv1 = nss_cms_after_end(p7dcx->childp7dcx) != SECSuccess; 395 p7dcx->childp7dcx = NULL; 396 } 397 /* Finish our asn1 decoder */ 398 if (p7dcx->dcx) { 399 rv2 = SEC_ASN1DecoderFinish(p7dcx->dcx); 400 p7dcx->dcx = NULL; 401 } 402 if (rv1 != SECSuccess || rv2 != SECSuccess || p7dcx->error != 0) { 403 return SECFailure; 404 } 405 406 switch (p7dcx->type) { 407 case SEC_OID_PKCS7_SIGNED_DATA: 408 if (p7dcx->content.signedData) 409 rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData); 410 break; 411 case SEC_OID_PKCS7_ENVELOPED_DATA: 412 if (p7dcx->content.envelopedData) 413 rv = NSS_CMSEnvelopedData_Decode_AfterEnd( 414 p7dcx->content.envelopedData); 415 break; 416 case SEC_OID_PKCS7_DIGESTED_DATA: 417 if (p7dcx->content.digestedData) 418 rv = NSS_CMSDigestedData_Decode_AfterEnd( 419 p7dcx->content.digestedData); 420 break; 421 case SEC_OID_PKCS7_ENCRYPTED_DATA: 422 if (p7dcx->content.encryptedData) 423 rv = NSS_CMSEncryptedData_Decode_AfterEnd( 424 p7dcx->content.encryptedData); 425 break; 426 case SEC_OID_PKCS7_DATA: 427 break; 428 default: 429 rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type, 430 p7dcx->content.genericData); 431 break; 432 } 433 return rv; 434 } 435 436 /* 437 * nss_cms_decoder_work_data - handle decoded data bytes. 438 * 439 * This function either decrypts the data if needed, and/or calculates digests 440 * on it, then either stores it or passes it on to the next level decoder. 441 */ 442 static void 443 nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, 444 const unsigned char *data, unsigned long len, 445 PRBool final) 446 { 447 NSSCMSContentInfo *cinfo; 448 unsigned char *buf = NULL; 449 unsigned char *dest; 450 unsigned int offset; 451 SECStatus rv; 452 453 /* 454 * We should really have data to process, or we should be trying 455 * to finish/flush the last block. (This is an overly paranoid 456 * check since all callers are in this file and simple inspection 457 * proves they do it right. But it could find a bug in future 458 * modifications/development, that is why it is here.) 459 */ 460 PORT_Assert((data != NULL && len) || final); 461 462 cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); 463 if (!cinfo) { 464 /* The original programmer didn't expect this to happen */ 465 p7dcx->error = SEC_ERROR_LIBRARY_FAILURE; 466 goto loser; 467 } 468 469 if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { 470 /* 471 * we are decrypting. 472 * 473 * XXX If we get an error, we do not want to do the digest or callback, 474 * but we want to keep decoding. Or maybe we want to stop decoding 475 * altogether if there is a callback, because obviously we are not 476 * sending the data back and they want to know that. 477 */ 478 479 unsigned int outlen = 0; /* length of decrypted data */ 480 unsigned int buflen; /* length available for decrypted data */ 481 482 /* find out about the length of decrypted data */ 483 buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final); 484 485 /* 486 * it might happen that we did not provide enough data for a full 487 * block (decryption unit), and that there is no output available 488 */ 489 490 /* no output available, AND no input? */ 491 if (buflen == 0 && len == 0) 492 goto loser; /* bail out */ 493 494 /* 495 * have inner decoder: pass the data on (means inner content type is NOT data) 496 * no inner decoder: we have DATA in here: either call callback or store 497 */ 498 if (buflen != 0) { 499 /* there will be some output - need to make room for it */ 500 /* allocate buffer from the heap */ 501 buf = (unsigned char *)PORT_Alloc(buflen); 502 if (buf == NULL) { 503 p7dcx->error = SEC_ERROR_NO_MEMORY; 504 goto loser; 505 } 506 } 507 508 /* 509 * decrypt incoming data 510 * buf can still be NULL here (and buflen == 0) here if we don't expect 511 * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to 512 * keep track of incoming data 513 */ 514 rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen, 515 data, len, final); 516 if (rv != SECSuccess) { 517 p7dcx->error = PORT_GetError(); 518 goto loser; 519 } 520 521 PORT_Assert(final || outlen == buflen); 522 523 /* swap decrypted data in */ 524 data = buf; 525 len = outlen; 526 } 527 528 if (len == 0) 529 goto done; /* nothing more to do */ 530 531 /* 532 * Update the running digests with plaintext bytes (if we need to). 533 */ 534 if (cinfo->privateInfo && cinfo->privateInfo->digcx) 535 NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); 536 537 /* at this point, we have the plain decoded & decrypted data 538 ** which is either more encoded DER (which we need to hand to the child 539 ** decoder) or data we need to hand back to our caller 540 */ 541 542 /* pass the content back to our caller or */ 543 /* feed our freshly decrypted and decoded data into child decoder */ 544 if (p7dcx->cb != NULL) { 545 (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len); 546 } 547 #if 1 548 else 549 #endif 550 if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) { 551 /* store it in "inner" data item as well */ 552 /* find the DATA item in the encapsulated cinfo and store it there */ 553 NSSCMSDecoderData *decoderData = 554 (NSSCMSDecoderData *)cinfo->content.pointer; 555 SECItem *dataItem = &decoderData->data; 556 557 offset = dataItem->len; 558 if (dataItem->len + len > decoderData->totalBufferSize) { 559 int needLen = (dataItem->len + len) * 2; 560 dest = (unsigned char *) 561 PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen); 562 if (dest == NULL) { 563 p7dcx->error = SEC_ERROR_NO_MEMORY; 564 goto loser; 565 } 566 567 if (dataItem->len) { 568 PORT_Memcpy(dest, dataItem->data, dataItem->len); 569 } 570 decoderData->totalBufferSize = needLen; 571 dataItem->data = dest; 572 } 573 574 /* copy it in */ 575 PORT_Memcpy(dataItem->data + offset, data, len); 576 dataItem->len += len; 577 } 578 579 done: 580 loser: 581 if (buf) 582 PORT_Free(buf); 583 } 584 585 /* 586 * nss_cms_decoder_update - deliver decoded data to the next higher level! 587 */ 588 static void 589 nss_cms_decoder_update(void *p7ecx, const char *data, unsigned long len) 590 { 591 (void)NSS_CMSDecoder_Update((NSSCMSDecoderContext *)p7ecx, data, len); 592 } 593 594 /* 595 * nss_cms_decoder_update_filter - process ASN.1 data 596 * 597 * once we have set up a filter in nss_cms_decoder_notify(), 598 * all data processed by the ASN.1 decoder is also passed through here. 599 * we pass the content bytes (as opposed to length and tag bytes) on to 600 * nss_cms_decoder_work_data(). 601 */ 602 static void 603 nss_cms_decoder_update_filter(void *arg, const char *data, unsigned long len, 604 int depth, SEC_ASN1EncodingPart data_kind) 605 { 606 NSSCMSDecoderContext *p7dcx; 607 608 PORT_Assert(len); /* paranoia */ 609 if (len == 0) 610 return; 611 612 p7dcx = (NSSCMSDecoderContext *)arg; 613 614 p7dcx->saw_contents = PR_TRUE; 615 616 /* pass on the content bytes only */ 617 if (data_kind == SEC_ASN1_Contents) 618 nss_cms_decoder_work_data(p7dcx, (const unsigned char *)data, len, 619 PR_FALSE); 620 } 621 622 /* 623 * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message 624 * 625 * "poolp" - pointer to arena for message, or NULL if new pool should be created 626 * "cb", "cb_arg" - callback function and argument for delivery of inner content 627 * "pwfn", pwfn_arg" - callback function for getting token password 628 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData 629 */ 630 NSSCMSDecoderContext * 631 NSS_CMSDecoder_Start(PLArenaPool *poolp, 632 NSSCMSContentCallback cb, void *cb_arg, 633 PK11PasswordFunc pwfn, void *pwfn_arg, 634 NSSCMSGetDecryptKeyCallback decrypt_key_cb, 635 void *decrypt_key_cb_arg) 636 { 637 NSSCMSDecoderContext *p7dcx; 638 NSSCMSMessage *cmsg; 639 640 cmsg = NSS_CMSMessage_Create(poolp); 641 if (cmsg == NULL) 642 return NULL; 643 644 NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, 645 decrypt_key_cb_arg, NULL, NULL); 646 647 p7dcx = PORT_ZNew(NSSCMSDecoderContext); 648 if (p7dcx == NULL) { 649 NSS_CMSMessage_Destroy(cmsg); 650 return NULL; 651 } 652 653 p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate); 654 if (p7dcx->dcx == NULL) { 655 PORT_Free(p7dcx); 656 NSS_CMSMessage_Destroy(cmsg); 657 return NULL; 658 } 659 660 SEC_ASN1DecoderSetNotifyProc(p7dcx->dcx, nss_cms_decoder_notify, p7dcx); 661 662 p7dcx->cmsg = cmsg; 663 p7dcx->type = SEC_OID_UNKNOWN; 664 665 p7dcx->cb = cb; 666 p7dcx->cb_arg = cb_arg; 667 p7dcx->first_decoded = PR_FALSE; 668 p7dcx->need_indefinite_finish = PR_FALSE; 669 return p7dcx; 670 } 671 672 /* 673 * NSS_CMSDecoder_Update - feed DER-encoded data to decoder 674 */ 675 SECStatus 676 NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, 677 unsigned long len) 678 { 679 SECStatus rv = SECSuccess; 680 if (p7dcx->dcx != NULL && p7dcx->error == 0) { 681 /* if error is set already, don't bother */ 682 if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) && (p7dcx->first_decoded == PR_TRUE) && (buf[0] == SEC_ASN1_INTEGER)) { 683 /* Microsoft Windows 2008 left out the Sequence wrapping in some 684 * of their kerberos replies. If we are here, we most likely are 685 * dealing with one of those replies. Supply the Sequence wrap 686 * as indefinite encoding (since we don't know the total length 687 * yet) */ 688 static const char lbuf[2] = { SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, 0x80 }; 689 rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf)); 690 if (rv != SECSuccess) { 691 goto loser; 692 } 693 /* ok, we're going to need the indefinite finish when we are done */ 694 p7dcx->need_indefinite_finish = PR_TRUE; 695 } 696 697 rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len); 698 } 699 700 loser: 701 p7dcx->first_decoded = PR_FALSE; 702 if (rv != SECSuccess) { 703 p7dcx->error = PORT_GetError(); 704 PORT_Assert(p7dcx->error); 705 if (p7dcx->error == 0) 706 p7dcx->error = -1; 707 } 708 709 if (p7dcx->error == 0) 710 return SECSuccess; 711 712 /* there has been a problem, let's finish the decoder */ 713 nss_cms_after_end(p7dcx); 714 PORT_SetError(p7dcx->error); 715 return SECFailure; 716 } 717 718 /* 719 * NSS_CMSDecoder_Cancel - stop decoding in case of error 720 */ 721 void 722 NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx) 723 { 724 nss_cms_after_end(p7dcx); 725 NSS_CMSMessage_Destroy(p7dcx->cmsg); 726 PORT_Free(p7dcx); 727 } 728 729 /* 730 * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding 731 */ 732 NSSCMSMessage * 733 NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx) 734 { 735 NSSCMSMessage *cmsg; 736 737 cmsg = p7dcx->cmsg; 738 739 if (nss_cms_after_end(p7dcx) != SECSuccess) { 740 NSS_CMSMessage_Destroy(cmsg); /* get rid of pool if it's ours */ 741 cmsg = NULL; 742 } 743 744 PORT_Free(p7dcx); 745 return cmsg; 746 } 747 748 NSSCMSMessage * 749 NSS_CMSMessage_CreateFromDER(SECItem *DERmessage, 750 NSSCMSContentCallback cb, void *cb_arg, 751 PK11PasswordFunc pwfn, void *pwfn_arg, 752 NSSCMSGetDecryptKeyCallback decrypt_key_cb, 753 void *decrypt_key_cb_arg) 754 { 755 NSSCMSDecoderContext *p7dcx; 756 757 /* first arg(poolp) == NULL => create our own pool */ 758 p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, 759 decrypt_key_cb, decrypt_key_cb_arg); 760 if (p7dcx == NULL) { 761 return NULL; 762 } 763 nss_cms_set_max_asn_length(p7dcx, DERmessage->len); 764 765 NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len); 766 return NSS_CMSDecoder_Finish(p7dcx); 767 }