p7common.c (19796B)
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 * PKCS7 implementation -- the exported parts that are used whether 7 * creating or decoding. 8 */ 9 10 #include "p7local.h" 11 12 #include "cert.h" 13 #include "secitem.h" 14 #include "secoid.h" 15 #include "pk11func.h" 16 17 /* 18 * Find out (saving pointer to lookup result for future reference) 19 * and return the inner content type. 20 */ 21 SECOidTag 22 SEC_PKCS7ContentType(SEC_PKCS7ContentInfo *cinfo) 23 { 24 if (cinfo->contentTypeTag == NULL) 25 cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); 26 27 if (cinfo->contentTypeTag == NULL) 28 return SEC_OID_UNKNOWN; 29 30 return cinfo->contentTypeTag->offset; 31 } 32 33 /* 34 * Destroy a PKCS7 contentInfo and all of its sub-pieces. 35 */ 36 void 37 SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo) 38 { 39 SECOidTag kind; 40 CERTCertificate **certs; 41 CERTCertificateList **certlists; 42 SEC_PKCS7SignerInfo **signerinfos; 43 SEC_PKCS7RecipientInfo **recipientinfos; 44 45 PORT_Assert(cinfo->refCount > 0); 46 if (cinfo->refCount <= 0) 47 return; 48 49 cinfo->refCount--; 50 if (cinfo->refCount > 0) 51 return; 52 53 certs = NULL; 54 certlists = NULL; 55 recipientinfos = NULL; 56 signerinfos = NULL; 57 58 kind = SEC_PKCS7ContentType(cinfo); 59 switch (kind) { 60 case SEC_OID_PKCS7_ENVELOPED_DATA: { 61 SEC_PKCS7EnvelopedData *edp; 62 63 edp = cinfo->content.envelopedData; 64 if (edp != NULL) { 65 recipientinfos = edp->recipientInfos; 66 } 67 } break; 68 case SEC_OID_PKCS7_SIGNED_DATA: { 69 SEC_PKCS7SignedData *sdp; 70 71 sdp = cinfo->content.signedData; 72 if (sdp != NULL) { 73 certs = sdp->certs; 74 certlists = sdp->certLists; 75 signerinfos = sdp->signerInfos; 76 } 77 } break; 78 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { 79 SEC_PKCS7SignedAndEnvelopedData *saedp; 80 81 saedp = cinfo->content.signedAndEnvelopedData; 82 if (saedp != NULL) { 83 certs = saedp->certs; 84 certlists = saedp->certLists; 85 recipientinfos = saedp->recipientInfos; 86 signerinfos = saedp->signerInfos; 87 if (saedp->sigKey != NULL) 88 PK11_FreeSymKey(saedp->sigKey); 89 } 90 } break; 91 default: 92 /* XXX Anything else that needs to be "manually" freed/destroyed? */ 93 break; 94 } 95 96 if (certs != NULL) { 97 CERTCertificate *cert; 98 99 while ((cert = *certs++) != NULL) { 100 CERT_DestroyCertificate(cert); 101 } 102 } 103 104 if (certlists != NULL) { 105 CERTCertificateList *certlist; 106 107 while ((certlist = *certlists++) != NULL) { 108 CERT_DestroyCertificateList(certlist); 109 } 110 } 111 112 if (recipientinfos != NULL) { 113 SEC_PKCS7RecipientInfo *ri; 114 115 while ((ri = *recipientinfos++) != NULL) { 116 if (ri->cert != NULL) 117 CERT_DestroyCertificate(ri->cert); 118 } 119 } 120 121 if (signerinfos != NULL) { 122 SEC_PKCS7SignerInfo *si; 123 124 while ((si = *signerinfos++) != NULL) { 125 if (si->cert != NULL) 126 CERT_DestroyCertificate(si->cert); 127 if (si->certList != NULL) 128 CERT_DestroyCertificateList(si->certList); 129 } 130 } 131 132 if (cinfo->poolp != NULL) { 133 PORT_FreeArena(cinfo->poolp, PR_FALSE); /* XXX clear it? */ 134 } 135 } 136 137 /* 138 * Return a copy of the given contentInfo. The copy may be virtual 139 * or may be real -- either way, the result needs to be passed to 140 * SEC_PKCS7DestroyContentInfo later (as does the original). 141 */ 142 SEC_PKCS7ContentInfo * 143 SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo) 144 { 145 if (cinfo == NULL) 146 return NULL; 147 148 PORT_Assert(cinfo->refCount > 0); 149 150 if (cinfo->created) { 151 /* 152 * Want to do a real copy of these; otherwise subsequent 153 * changes made to either copy are likely to be a surprise. 154 * XXX I suspect that this will not actually be called for yet, 155 * which is why the assert, so to notice if it is... 156 */ 157 PORT_Assert(0); 158 /* 159 * XXX Create a new pool here, and copy everything from 160 * within. For cert stuff, need to call the appropriate 161 * copy functions, etc. 162 */ 163 } 164 165 cinfo->refCount++; 166 return cinfo; 167 } 168 169 /* 170 * Return a pointer to the actual content. In the case of those types 171 * which are encrypted, this returns the *plain* content. 172 * XXX Needs revisiting if/when we handle nested encrypted types. 173 */ 174 SECItem * 175 SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo) 176 { 177 SECOidTag kind; 178 179 kind = SEC_PKCS7ContentType(cinfo); 180 switch (kind) { 181 case SEC_OID_PKCS7_DATA: 182 return cinfo->content.data; 183 case SEC_OID_PKCS7_DIGESTED_DATA: { 184 SEC_PKCS7DigestedData *digd; 185 186 digd = cinfo->content.digestedData; 187 if (digd == NULL) 188 break; 189 return SEC_PKCS7GetContent(&(digd->contentInfo)); 190 } 191 case SEC_OID_PKCS7_ENCRYPTED_DATA: { 192 SEC_PKCS7EncryptedData *encd; 193 194 encd = cinfo->content.encryptedData; 195 if (encd == NULL) 196 break; 197 return &(encd->encContentInfo.plainContent); 198 } 199 case SEC_OID_PKCS7_ENVELOPED_DATA: { 200 SEC_PKCS7EnvelopedData *envd; 201 202 envd = cinfo->content.envelopedData; 203 if (envd == NULL) 204 break; 205 return &(envd->encContentInfo.plainContent); 206 } 207 case SEC_OID_PKCS7_SIGNED_DATA: { 208 SEC_PKCS7SignedData *sigd; 209 210 sigd = cinfo->content.signedData; 211 if (sigd == NULL) 212 break; 213 return SEC_PKCS7GetContent(&(sigd->contentInfo)); 214 } 215 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { 216 SEC_PKCS7SignedAndEnvelopedData *saed; 217 218 saed = cinfo->content.signedAndEnvelopedData; 219 if (saed == NULL) 220 break; 221 return &(saed->encContentInfo.plainContent); 222 } 223 default: 224 PORT_Assert(0); 225 break; 226 } 227 228 return NULL; 229 } 230 231 /* 232 * XXX Fix the placement and formatting of the 233 * following routines (i.e. make them consistent with the rest of 234 * the pkcs7 code -- I think some/many belong in other files and 235 * they all need a formatting/style rehaul) 236 */ 237 238 /* retrieve the algorithm identifier for encrypted data. 239 * the identifier returned is a copy of the algorithm identifier 240 * in the content info and needs to be freed after being used. 241 * 242 * cinfo is the content info for which to retrieve the 243 * encryption algorithm. 244 * 245 * if the content info is not encrypted data or an error 246 * occurs NULL is returned. 247 */ 248 SECAlgorithmID * 249 SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo) 250 { 251 SECAlgorithmID *alg = 0; 252 switch (SEC_PKCS7ContentType(cinfo)) { 253 case SEC_OID_PKCS7_ENCRYPTED_DATA: 254 alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg; 255 break; 256 case SEC_OID_PKCS7_ENVELOPED_DATA: 257 alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg; 258 break; 259 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: 260 alg = &cinfo->content.signedAndEnvelopedData 261 ->encContentInfo.contentEncAlg; 262 break; 263 default: 264 alg = 0; 265 break; 266 } 267 268 return alg; 269 } 270 271 /* set the content of the content info. For data content infos, 272 * the data is set. For encrytped content infos, the plainContent 273 * is set, and is expected to be encrypted later. 274 * 275 * cinfo is the content info where the data will be set 276 * 277 * buf is a buffer of the data to set 278 * 279 * len is the length of the data being set. 280 * 281 * in the event of an error, SECFailure is returned. SECSuccess 282 * indicates the content was successfully set. 283 */ 284 SECStatus 285 SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo, 286 const char *buf, 287 unsigned long len) 288 { 289 SECOidTag cinfo_type; 290 SECStatus rv; 291 SECItem content; 292 SECOidData *contentTypeTag = NULL; 293 294 content.type = siBuffer; 295 content.data = (unsigned char *)buf; 296 content.len = len; 297 298 cinfo_type = SEC_PKCS7ContentType(cinfo); 299 300 /* set inner content */ 301 switch (cinfo_type) { 302 case SEC_OID_PKCS7_SIGNED_DATA: 303 if (content.len > 0) { 304 /* we "leak" the old content here, but as it's all in the pool */ 305 /* it does not really matter */ 306 307 /* create content item if necessary */ 308 if (cinfo->content.signedData->contentInfo.content.data == NULL) 309 cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0); 310 rv = SECITEM_CopyItem(cinfo->poolp, 311 cinfo->content.signedData->contentInfo.content.data, 312 &content); 313 } else { 314 cinfo->content.signedData->contentInfo.content.data->data = NULL; 315 cinfo->content.signedData->contentInfo.content.data->len = 0; 316 rv = SECSuccess; 317 } 318 if (rv == SECFailure) 319 goto loser; 320 321 break; 322 case SEC_OID_PKCS7_ENCRYPTED_DATA: 323 /* XXX this forces the inner content type to be "data" */ 324 /* do we really want to override without asking or reason? */ 325 contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA); 326 if (contentTypeTag == NULL) 327 goto loser; 328 rv = SECITEM_CopyItem(cinfo->poolp, 329 &(cinfo->content.encryptedData->encContentInfo.contentType), 330 &(contentTypeTag->oid)); 331 if (rv == SECFailure) 332 goto loser; 333 if (content.len > 0) { 334 rv = SECITEM_CopyItem(cinfo->poolp, 335 &(cinfo->content.encryptedData->encContentInfo.plainContent), 336 &content); 337 } else { 338 cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL; 339 cinfo->content.encryptedData->encContentInfo.encContent.data = NULL; 340 cinfo->content.encryptedData->encContentInfo.plainContent.len = 0; 341 cinfo->content.encryptedData->encContentInfo.encContent.len = 0; 342 rv = SECSuccess; 343 } 344 if (rv == SECFailure) 345 goto loser; 346 break; 347 case SEC_OID_PKCS7_DATA: 348 cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp, 349 sizeof(SECItem)); 350 if (cinfo->content.data == NULL) 351 goto loser; 352 if (content.len > 0) { 353 rv = SECITEM_CopyItem(cinfo->poolp, 354 cinfo->content.data, &content); 355 } else { 356 /* handle case with NULL content */ 357 rv = SECSuccess; 358 } 359 if (rv == SECFailure) 360 goto loser; 361 break; 362 default: 363 goto loser; 364 } 365 366 return SECSuccess; 367 368 loser: 369 370 return SECFailure; 371 } 372 373 /* the content of an encrypted data content info is encrypted. 374 * it is assumed that for encrypted data, that the data has already 375 * been set and is in the "plainContent" field of the content info. 376 * 377 * cinfo is the content info to encrypt 378 * 379 * key is the key with which to perform the encryption. if the 380 * algorithm is a password based encryption algorithm, the 381 * key is actually a password which will be processed per 382 * PKCS #5. 383 * 384 * in the event of an error, SECFailure is returned. SECSuccess 385 * indicates a success. 386 */ 387 SECStatus 388 SEC_PKCS7EncryptContents(PLArenaPool *poolp, 389 SEC_PKCS7ContentInfo *cinfo, 390 SECItem *key, 391 void *wincx) 392 { 393 SECAlgorithmID *algid = NULL; 394 SECItem *src; 395 SECItem *dest; 396 SECItem *blocked_data = NULL; 397 void *mark; 398 void *cx; 399 PK11SymKey *eKey = NULL; 400 PK11SlotInfo *slot = NULL; 401 402 CK_MECHANISM_TYPE cryptoMechType; 403 int bs; 404 SECStatus rv = SECFailure; 405 SECItem *c_param = NULL; 406 407 if ((cinfo == NULL) || (key == NULL)) 408 return SECFailure; 409 410 if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) 411 return SECFailure; 412 413 algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); 414 if (algid == NULL) 415 return SECFailure; 416 417 if (poolp == NULL) 418 poolp = cinfo->poolp; 419 420 mark = PORT_ArenaMark(poolp); 421 422 src = &cinfo->content.encryptedData->encContentInfo.plainContent; 423 dest = &cinfo->content.encryptedData->encContentInfo.encContent; 424 dest->data = (unsigned char *)PORT_ArenaZAlloc(poolp, (src->len + 64)); 425 dest->len = (src->len + 64); 426 if (dest->data == NULL) { 427 rv = SECFailure; 428 goto loser; 429 } 430 431 slot = PK11_GetInternalKeySlot(); 432 if (slot == NULL) { 433 rv = SECFailure; 434 goto loser; 435 } 436 437 eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); 438 if (eKey == NULL) { 439 rv = SECFailure; 440 goto loser; 441 } 442 443 cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key); 444 if (cryptoMechType == CKM_INVALID_MECHANISM) { 445 rv = SECFailure; 446 goto loser; 447 } 448 449 /* block according to PKCS 8 */ 450 bs = PK11_GetBlockSize(cryptoMechType, c_param); 451 rv = SECSuccess; 452 if (bs) { 453 char pad_char; 454 pad_char = (char)(bs - (src->len % bs)); 455 if (src->len % bs) { 456 rv = SECSuccess; 457 blocked_data = PK11_BlockData(src, bs); 458 if (blocked_data) { 459 PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char), 460 pad_char, (int)pad_char); 461 } else { 462 rv = SECFailure; 463 goto loser; 464 } 465 } else { 466 blocked_data = SECITEM_DupItem(src); 467 if (blocked_data) { 468 blocked_data->data = (unsigned char *)PORT_Realloc( 469 blocked_data->data, 470 blocked_data->len + bs); 471 if (blocked_data->data) { 472 blocked_data->len += bs; 473 PORT_Memset((blocked_data->data + src->len), (char)bs, bs); 474 } else { 475 rv = SECFailure; 476 goto loser; 477 } 478 } else { 479 rv = SECFailure; 480 goto loser; 481 } 482 } 483 } else { 484 blocked_data = SECITEM_DupItem(src); 485 if (!blocked_data) { 486 rv = SECFailure; 487 goto loser; 488 } 489 } 490 491 cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, 492 eKey, c_param); 493 if (cx == NULL) { 494 rv = SECFailure; 495 goto loser; 496 } 497 498 rv = PK11_CipherOp((PK11Context *)cx, dest->data, (int *)(&dest->len), 499 (int)(src->len + 64), blocked_data->data, 500 (int)blocked_data->len); 501 PK11_DestroyContext((PK11Context *)cx, PR_TRUE); 502 503 loser: 504 /* let success fall through */ 505 if (blocked_data != NULL) 506 SECITEM_ZfreeItem(blocked_data, PR_TRUE); 507 508 if (rv == SECFailure) 509 PORT_ArenaRelease(poolp, mark); 510 else 511 PORT_ArenaUnmark(poolp, mark); 512 513 if (eKey != NULL) 514 PK11_FreeSymKey(eKey); 515 516 if (slot != NULL) 517 PK11_FreeSlot(slot); 518 519 if (c_param != NULL) 520 SECITEM_ZfreeItem(c_param, PR_TRUE); 521 522 return rv; 523 } 524 525 /* the content of an encrypted data content info is decrypted. 526 * it is assumed that for encrypted data, that the data has already 527 * been set and is in the "encContent" field of the content info. 528 * 529 * cinfo is the content info to decrypt 530 * 531 * key is the key with which to perform the decryption. if the 532 * algorithm is a password based encryption algorithm, the 533 * key is actually a password which will be processed per 534 * PKCS #5. 535 * 536 * in the event of an error, SECFailure is returned. SECSuccess 537 * indicates a success. 538 */ 539 SECStatus 540 SEC_PKCS7DecryptContents(PLArenaPool *poolp, 541 SEC_PKCS7ContentInfo *cinfo, 542 SECItem *key, 543 void *wincx) 544 { 545 SECAlgorithmID *algid = NULL; 546 SECStatus rv = SECFailure; 547 SECItem *dest, *src; 548 void *mark; 549 550 PK11SymKey *eKey = NULL; 551 PK11SlotInfo *slot = NULL; 552 CK_MECHANISM_TYPE cryptoMechType; 553 void *cx; 554 SECItem *c_param = NULL; 555 int bs; 556 557 if ((cinfo == NULL) || (key == NULL)) 558 return SECFailure; 559 560 if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) 561 return SECFailure; 562 563 algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); 564 if (algid == NULL) 565 return SECFailure; 566 567 if (poolp == NULL) 568 poolp = cinfo->poolp; 569 570 mark = PORT_ArenaMark(poolp); 571 572 src = &cinfo->content.encryptedData->encContentInfo.encContent; 573 dest = &cinfo->content.encryptedData->encContentInfo.plainContent; 574 dest->data = (unsigned char *)PORT_ArenaZAlloc(poolp, (src->len + 64)); 575 dest->len = (src->len + 64); 576 if (dest->data == NULL) { 577 rv = SECFailure; 578 goto loser; 579 } 580 581 slot = PK11_GetInternalKeySlot(); 582 if (slot == NULL) { 583 rv = SECFailure; 584 goto loser; 585 } 586 587 eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); 588 if (eKey == NULL) { 589 rv = SECFailure; 590 goto loser; 591 } 592 593 cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key); 594 if (cryptoMechType == CKM_INVALID_MECHANISM) { 595 rv = SECFailure; 596 goto loser; 597 } 598 599 cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, 600 eKey, c_param); 601 if (cx == NULL) { 602 rv = SECFailure; 603 goto loser; 604 } 605 606 rv = PK11_CipherOp((PK11Context *)cx, dest->data, (int *)(&dest->len), 607 (int)(src->len + 64), src->data, (int)src->len); 608 PK11_DestroyContext((PK11Context *)cx, PR_TRUE); 609 610 bs = PK11_GetBlockSize(cryptoMechType, c_param); 611 if (bs) { 612 /* check for proper badding in block algorithms. this assumes 613 * RC2 cbc or a DES cbc variant. and the padding is thus defined 614 */ 615 if (((int)dest->data[dest->len - 1] <= bs) && 616 ((int)dest->data[dest->len - 1] > 0)) { 617 dest->len -= (int)dest->data[dest->len - 1]; 618 } else { 619 rv = SECFailure; 620 /* set an error ? */ 621 } 622 } 623 624 loser: 625 /* let success fall through */ 626 if (rv == SECFailure) 627 PORT_ArenaRelease(poolp, mark); 628 else 629 PORT_ArenaUnmark(poolp, mark); 630 631 if (eKey != NULL) 632 PK11_FreeSymKey(eKey); 633 634 if (slot != NULL) 635 PK11_FreeSlot(slot); 636 637 if (c_param != NULL) 638 SECITEM_ZfreeItem(c_param, PR_TRUE); 639 640 return rv; 641 } 642 643 SECItem ** 644 SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo) 645 { 646 switch (SEC_PKCS7ContentType(cinfo)) { 647 case SEC_OID_PKCS7_SIGNED_DATA: 648 return cinfo->content.signedData->rawCerts; 649 break; 650 default: 651 return NULL; 652 break; 653 } 654 } 655 656 int 657 SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo) 658 { 659 if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA) 660 return cinfo->content.envelopedData->encContentInfo.keysize; 661 else 662 return 0; 663 }