OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/child/webcrypto/shared_crypto.h" | 5 #include "content/child/webcrypto/shared_crypto.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "content/child/webcrypto/crypto_data.h" | 8 #include "content/child/webcrypto/crypto_data.h" |
9 #include "content/child/webcrypto/jwk.h" | 9 #include "content/child/webcrypto/jwk.h" |
10 #include "content/child/webcrypto/platform_crypto.h" | 10 #include "content/child/webcrypto/platform_crypto.h" |
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 const blink::WebCryptoAlgorithm& wrapping_algorithm, | 431 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
432 const blink::WebCryptoAlgorithm& algorithm, | 432 const blink::WebCryptoAlgorithm& algorithm, |
433 bool extractable, | 433 bool extractable, |
434 blink::WebCryptoKeyUsageMask usage_mask, | 434 blink::WebCryptoKeyUsageMask usage_mask, |
435 blink::WebCryptoKey* key) { | 435 blink::WebCryptoKey* key) { |
436 std::vector<uint8> buffer; | 436 std::vector<uint8> buffer; |
437 Status status = DecryptDontCheckKeyUsage( | 437 Status status = DecryptDontCheckKeyUsage( |
438 wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); | 438 wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); |
439 if (status.IsError()) | 439 if (status.IsError()) |
440 return status; | 440 return status; |
441 status = ImportKey( | 441 // NOTE that returning the details of ImportKey() failures may leak |
| 442 // information about the plaintext of the encrypted key (for instance the JWK |
| 443 // key_ops). As long as the ImportKey error messages don't describe actual |
| 444 // key bytes however this should be OK. For more discussion see |
| 445 // http://crubg.com/372040 |
| 446 return ImportKey( |
442 format, CryptoData(buffer), algorithm, extractable, usage_mask, key); | 447 format, CryptoData(buffer), algorithm, extractable, usage_mask, key); |
443 // NOTE! Returning the details of any ImportKey() failure here would leak | |
444 // information about the plaintext internals of the encrypted key. Instead, | |
445 // collapse any error into the generic Status::OperationError(). | |
446 return status.IsError() ? Status::OperationError() : Status::Success(); | |
447 } | 448 } |
448 | 449 |
449 Status WrapKeyExportAndEncrypt( | 450 Status WrapKeyExportAndEncrypt( |
450 blink::WebCryptoKeyFormat format, | 451 blink::WebCryptoKeyFormat format, |
451 const blink::WebCryptoKey& key_to_wrap, | 452 const blink::WebCryptoKey& key_to_wrap, |
452 const blink::WebCryptoKey& wrapping_key, | 453 const blink::WebCryptoKey& wrapping_key, |
453 const blink::WebCryptoAlgorithm& wrapping_algorithm, | 454 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
454 std::vector<uint8>* buffer) { | 455 std::vector<uint8>* buffer) { |
455 std::vector<uint8> exported_data; | 456 std::vector<uint8> exported_data; |
456 Status status = ExportKey(format, key_to_wrap, &exported_data); | 457 Status status = ExportKey(format, key_to_wrap, &exported_data); |
(...skipping 11 matching lines...) Expand all Loading... |
468 return 64; | 469 return 64; |
469 case blink::WebCryptoAlgorithmIdSha384: | 470 case blink::WebCryptoAlgorithmIdSha384: |
470 case blink::WebCryptoAlgorithmIdSha512: | 471 case blink::WebCryptoAlgorithmIdSha512: |
471 return 128; | 472 return 128; |
472 default: | 473 default: |
473 NOTREACHED(); | 474 NOTREACHED(); |
474 return 0; | 475 return 0; |
475 } | 476 } |
476 } | 477 } |
477 | 478 |
| 479 // Returns the mask of all key usages that are possible for |algorithm| and |
| 480 // |key_type|. If the combination of |algorithm| and |key_type| doesn't make |
| 481 // sense, then returns 0 (no usages). |
| 482 blink::WebCryptoKeyUsageMask GetValidKeyUsagesForKeyType( |
| 483 blink::WebCryptoAlgorithmId algorithm, |
| 484 blink::WebCryptoKeyType key_type) { |
| 485 if (IsAlgorithmAsymmetric(algorithm) == |
| 486 (key_type == blink::WebCryptoKeyTypeSecret)) |
| 487 return 0; |
| 488 |
| 489 switch (algorithm) { |
| 490 case blink::WebCryptoAlgorithmIdAesCbc: |
| 491 case blink::WebCryptoAlgorithmIdAesGcm: |
| 492 case blink::WebCryptoAlgorithmIdAesCtr: |
| 493 return blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| 494 blink::WebCryptoKeyUsageWrapKey | |
| 495 blink::WebCryptoKeyUsageUnwrapKey; |
| 496 case blink::WebCryptoAlgorithmIdAesKw: |
| 497 return blink::WebCryptoKeyUsageWrapKey | |
| 498 blink::WebCryptoKeyUsageUnwrapKey; |
| 499 case blink::WebCryptoAlgorithmIdHmac: |
| 500 return blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| 501 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| 502 switch (key_type) { |
| 503 case blink::WebCryptoKeyTypePublic: |
| 504 return blink::WebCryptoKeyUsageVerify; |
| 505 case blink::WebCryptoKeyTypePrivate: |
| 506 return blink::WebCryptoKeyUsageSign; |
| 507 default: |
| 508 return 0; |
| 509 } |
| 510 case blink::WebCryptoAlgorithmIdRsaOaep: |
| 511 switch (key_type) { |
| 512 case blink::WebCryptoKeyTypePublic: |
| 513 return blink::WebCryptoKeyUsageEncrypt | |
| 514 blink::WebCryptoKeyUsageWrapKey; |
| 515 case blink::WebCryptoKeyTypePrivate: |
| 516 return blink::WebCryptoKeyUsageDecrypt | |
| 517 blink::WebCryptoKeyUsageUnwrapKey; |
| 518 default: |
| 519 return 0; |
| 520 } |
| 521 default: |
| 522 return 0; |
| 523 } |
| 524 } |
| 525 |
| 526 // Returns Status::Success() if |usages| is a valid set of key usages for |
| 527 // |algorithm| and |key_type|. Otherwise returns an error. |
| 528 // In the case of JWK format the check is incomplete for asymmetric algorithms. |
| 529 Status BestEffortCheckKeyUsagesForImport(blink::WebCryptoAlgorithmId algorithm, |
| 530 blink::WebCryptoKeyFormat format, |
| 531 blink::WebCryptoKeyUsageMask usages) { |
| 532 if (!IsAlgorithmAsymmetric(algorithm)) |
| 533 return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypeSecret, usages); |
| 534 |
| 535 // Try to infer the key type given the import format. |
| 536 blink::WebCryptoKeyType key_type; |
| 537 bool key_type_known = false; |
| 538 |
| 539 switch (format) { |
| 540 case blink::WebCryptoKeyFormatRaw: |
| 541 // TODO(eroman): The spec defines Diffie-Hellman raw import for public |
| 542 // keys, so this will need to be updated in the future when DH is |
| 543 // implemented. |
| 544 return Status::ErrorUnexpected(); |
| 545 case blink::WebCryptoKeyFormatSpki: |
| 546 key_type = blink::WebCryptoKeyTypePublic; |
| 547 key_type_known = true; |
| 548 break; |
| 549 case blink::WebCryptoKeyFormatPkcs8: |
| 550 key_type = blink::WebCryptoKeyTypePrivate; |
| 551 key_type_known = true; |
| 552 break; |
| 553 case blink::WebCryptoKeyFormatJwk: |
| 554 key_type_known = false; |
| 555 break; |
| 556 default: |
| 557 return Status::ErrorUnexpected(); |
| 558 } |
| 559 |
| 560 if (key_type_known) |
| 561 return CheckKeyUsages(algorithm, key_type, usages); |
| 562 |
| 563 // If the key type is not known, then the algorithm is asymmetric. Whether the |
| 564 // key data describes a public or private key isn't known yet. But it must at |
| 565 // least be ONE of those two. |
| 566 DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| 567 |
| 568 if (CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages) |
| 569 .IsError() && |
| 570 CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages) |
| 571 .IsError()) { |
| 572 return Status::ErrorCreateKeyBadUsages(); |
| 573 } |
| 574 |
| 575 return Status::Success(); |
| 576 } |
| 577 |
| 578 // Returns an error if |combined_usage_mask| is invalid for generating a key |
| 579 // pair for |algorithm|. Otherwise returns Status::Success(), and fills |
| 580 // |public_key_usages| with the usages for the public key, and |
| 581 // |private_key_usages| with those for the private key. |
| 582 Status CheckKeyUsagesForGenerateKeyPair( |
| 583 blink::WebCryptoAlgorithmId algorithm, |
| 584 blink::WebCryptoKeyUsageMask combined_usage_mask, |
| 585 blink::WebCryptoKeyUsageMask* public_key_usages, |
| 586 blink::WebCryptoKeyUsageMask* private_key_usages) { |
| 587 DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| 588 |
| 589 blink::WebCryptoKeyUsageMask all_public_key_usages = |
| 590 GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePublic); |
| 591 blink::WebCryptoKeyUsageMask all_private_key_usages = |
| 592 GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePrivate); |
| 593 |
| 594 if (!ContainsKeyUsages(all_public_key_usages | all_private_key_usages, |
| 595 combined_usage_mask)) |
| 596 return Status::ErrorCreateKeyBadUsages(); |
| 597 |
| 598 *public_key_usages = combined_usage_mask & all_public_key_usages; |
| 599 *private_key_usages = combined_usage_mask & all_private_key_usages; |
| 600 |
| 601 return Status::Success(); |
| 602 } |
| 603 |
478 } // namespace | 604 } // namespace |
479 | 605 |
480 void Init() { platform::Init(); } | 606 void Init() { platform::Init(); } |
481 | 607 |
482 Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, | 608 Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, |
483 const blink::WebCryptoKey& key, | 609 const blink::WebCryptoKey& key, |
484 const CryptoData& data, | 610 const CryptoData& data, |
485 std::vector<uint8>* buffer) { | 611 std::vector<uint8>* buffer) { |
486 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) | 612 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) |
487 return Status::ErrorUnexpected(); | 613 return Status::ErrorUnexpected(); |
(...skipping 25 matching lines...) Expand all Loading... |
513 | 639 |
514 scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( | 640 scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( |
515 blink::WebCryptoAlgorithmId algorithm) { | 641 blink::WebCryptoAlgorithmId algorithm) { |
516 return platform::CreateDigestor(algorithm); | 642 return platform::CreateDigestor(algorithm); |
517 } | 643 } |
518 | 644 |
519 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, | 645 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, |
520 bool extractable, | 646 bool extractable, |
521 blink::WebCryptoKeyUsageMask usage_mask, | 647 blink::WebCryptoKeyUsageMask usage_mask, |
522 blink::WebCryptoKey* key) { | 648 blink::WebCryptoKey* key) { |
| 649 Status status = |
| 650 CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask); |
| 651 if (status.IsError()) |
| 652 return status; |
| 653 |
523 unsigned int keylen_bytes = 0; | 654 unsigned int keylen_bytes = 0; |
524 | 655 |
525 // Get the secret key length in bytes from generation parameters. | 656 // Get the secret key length in bytes from generation parameters. |
526 // This resolves any defaults. | 657 // This resolves any defaults. |
527 switch (algorithm.id()) { | 658 switch (algorithm.id()) { |
528 case blink::WebCryptoAlgorithmIdAesCbc: | 659 case blink::WebCryptoAlgorithmIdAesCbc: |
529 case blink::WebCryptoAlgorithmIdAesGcm: | 660 case blink::WebCryptoAlgorithmIdAesGcm: |
530 case blink::WebCryptoAlgorithmIdAesKw: { | 661 case blink::WebCryptoAlgorithmIdAesKw: { |
531 if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) | 662 if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) |
532 return Status::ErrorGenerateKeyLength(); | 663 return Status::ErrorGenerateKeyLength(); |
(...skipping 24 matching lines...) Expand all Loading... |
557 // probably be able to allowed to generate them too. | 688 // probably be able to allowed to generate them too. |
558 if (keylen_bytes == 0) | 689 if (keylen_bytes == 0) |
559 return Status::ErrorGenerateKeyLength(); | 690 return Status::ErrorGenerateKeyLength(); |
560 | 691 |
561 return platform::GenerateSecretKey( | 692 return platform::GenerateSecretKey( |
562 algorithm, extractable, usage_mask, keylen_bytes, key); | 693 algorithm, extractable, usage_mask, keylen_bytes, key); |
563 } | 694 } |
564 | 695 |
565 Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, | 696 Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, |
566 bool extractable, | 697 bool extractable, |
567 blink::WebCryptoKeyUsageMask usage_mask, | 698 blink::WebCryptoKeyUsageMask combined_usage_mask, |
568 blink::WebCryptoKey* public_key, | 699 blink::WebCryptoKey* public_key, |
569 blink::WebCryptoKey* private_key) { | 700 blink::WebCryptoKey* private_key) { |
| 701 blink::WebCryptoKeyUsageMask public_key_usage_mask = 0; |
| 702 blink::WebCryptoKeyUsageMask private_key_usage_mask = 0; |
| 703 |
| 704 Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(), |
| 705 combined_usage_mask, |
| 706 &public_key_usage_mask, |
| 707 &private_key_usage_mask); |
| 708 if (status.IsError()) |
| 709 return status; |
| 710 |
570 // TODO(padolph): Handle other asymmetric algorithm key generation. | 711 // TODO(padolph): Handle other asymmetric algorithm key generation. |
571 switch (algorithm.paramsType()) { | 712 switch (algorithm.paramsType()) { |
572 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { | 713 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { |
573 const blink::WebCryptoRsaHashedKeyGenParams* params = | 714 const blink::WebCryptoRsaHashedKeyGenParams* params = |
574 algorithm.rsaHashedKeyGenParams(); | 715 algorithm.rsaHashedKeyGenParams(); |
575 | 716 |
576 if (!params->modulusLengthBits()) | 717 if (!params->modulusLengthBits()) |
577 return Status::ErrorGenerateRsaZeroModulus(); | 718 return Status::ErrorGenerateRsaZeroModulus(); |
578 | 719 |
579 CryptoData publicExponent(params->publicExponent()); | 720 CryptoData publicExponent(params->publicExponent()); |
580 if (!publicExponent.byte_length()) | 721 if (!publicExponent.byte_length()) |
581 return Status::ErrorGenerateKeyPublicExponent(); | 722 return Status::ErrorGenerateKeyPublicExponent(); |
582 | 723 |
583 return platform::GenerateRsaKeyPair(algorithm, | 724 return platform::GenerateRsaKeyPair(algorithm, |
584 extractable, | 725 extractable, |
585 usage_mask, | 726 public_key_usage_mask, |
| 727 private_key_usage_mask, |
586 params->modulusLengthBits(), | 728 params->modulusLengthBits(), |
587 publicExponent, | 729 publicExponent, |
588 public_key, | 730 public_key, |
589 private_key); | 731 private_key); |
590 } | 732 } |
591 default: | 733 default: |
592 return Status::ErrorUnsupported(); | 734 return Status::ErrorUnsupported(); |
593 } | 735 } |
594 } | 736 } |
595 | 737 |
596 // Note that this function may be called from the target Blink thread. | 738 // Note that this function may be called from the target Blink thread. |
597 Status ImportKey(blink::WebCryptoKeyFormat format, | 739 Status ImportKey(blink::WebCryptoKeyFormat format, |
598 const CryptoData& key_data, | 740 const CryptoData& key_data, |
599 const blink::WebCryptoAlgorithm& algorithm, | 741 const blink::WebCryptoAlgorithm& algorithm, |
600 bool extractable, | 742 bool extractable, |
601 blink::WebCryptoKeyUsageMask usage_mask, | 743 blink::WebCryptoKeyUsageMask usage_mask, |
602 blink::WebCryptoKey* key) { | 744 blink::WebCryptoKey* key) { |
| 745 // This is "best effort" because it is incomplete for JWK (for which the key |
| 746 // type is not yet known). ImportKeyJwk() does extra checks on key usage once |
| 747 // the key type has been determined. |
| 748 Status status = |
| 749 BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| 750 if (status.IsError()) |
| 751 return status; |
| 752 |
603 switch (format) { | 753 switch (format) { |
604 case blink::WebCryptoKeyFormatRaw: | 754 case blink::WebCryptoKeyFormatRaw: |
605 return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); | 755 return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); |
606 case blink::WebCryptoKeyFormatSpki: | 756 case blink::WebCryptoKeyFormatSpki: |
607 return platform::ImportKeySpki( | 757 return platform::ImportKeySpki( |
608 algorithm, key_data, extractable, usage_mask, key); | 758 algorithm, key_data, extractable, usage_mask, key); |
609 case blink::WebCryptoKeyFormatPkcs8: | 759 case blink::WebCryptoKeyFormatPkcs8: |
610 return platform::ImportKeyPkcs8( | 760 return platform::ImportKeyPkcs8( |
611 algorithm, key_data, extractable, usage_mask, key); | 761 algorithm, key_data, extractable, usage_mask, key); |
612 case blink::WebCryptoKeyFormatJwk: | 762 case blink::WebCryptoKeyFormatJwk: |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
743 const blink::WebCryptoAlgorithm& wrapping_algorithm, | 893 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
744 const blink::WebCryptoAlgorithm& algorithm, | 894 const blink::WebCryptoAlgorithm& algorithm, |
745 bool extractable, | 895 bool extractable, |
746 blink::WebCryptoKeyUsageMask usage_mask, | 896 blink::WebCryptoKeyUsageMask usage_mask, |
747 blink::WebCryptoKey* key) { | 897 blink::WebCryptoKey* key) { |
748 if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) | 898 if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) |
749 return Status::ErrorUnexpected(); | 899 return Status::ErrorUnexpected(); |
750 if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) | 900 if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) |
751 return Status::ErrorUnexpected(); | 901 return Status::ErrorUnexpected(); |
752 | 902 |
| 903 // Fail-fast if the key usages don't make sense. This avoids decrypting the |
| 904 // key only to then have import fail. It is "best effort" because when |
| 905 // unwrapping JWK for asymmetric algorithms the key type isn't known yet. |
| 906 Status status = |
| 907 BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| 908 if (status.IsError()) |
| 909 return status; |
| 910 |
753 switch (format) { | 911 switch (format) { |
754 case blink::WebCryptoKeyFormatRaw: | 912 case blink::WebCryptoKeyFormatRaw: |
755 if (wrapping_algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) { | 913 if (wrapping_algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) { |
756 // AES-KW is a special case, due to NSS's implementation only | 914 // AES-KW is a special case, due to NSS's implementation only |
757 // supporting C_Wrap/C_Unwrap with AES-KW | 915 // supporting C_Wrap/C_Unwrap with AES-KW |
758 return UnwrapKeyRaw(wrapped_key_data, | 916 return UnwrapKeyRaw(wrapped_key_data, |
759 wrapping_key, | 917 wrapping_key, |
760 wrapping_algorithm, | 918 wrapping_algorithm, |
761 algorithm, | 919 algorithm, |
762 extractable, | 920 extractable, |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
839 } | 997 } |
840 | 998 |
841 Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, | 999 Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, |
842 platform::PrivateKey** out) { | 1000 platform::PrivateKey** out) { |
843 *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); | 1001 *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); |
844 if (!*out) | 1002 if (!*out) |
845 return Status::ErrorUnexpectedKeyType(); | 1003 return Status::ErrorUnexpectedKeyType(); |
846 return Status::Success(); | 1004 return Status::Success(); |
847 } | 1005 } |
848 | 1006 |
| 1007 // Returns Status::Success() if |usages| is a valid set of key usages for |
| 1008 // |algorithm| and |key_type|. Otherwise returns an error. |
| 1009 Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm, |
| 1010 blink::WebCryptoKeyType key_type, |
| 1011 blink::WebCryptoKeyUsageMask usages) { |
| 1012 if (!ContainsKeyUsages(GetValidKeyUsagesForKeyType(algorithm, key_type), |
| 1013 usages)) |
| 1014 return Status::ErrorCreateKeyBadUsages(); |
| 1015 |
| 1016 return Status::Success(); |
| 1017 } |
| 1018 |
849 } // namespace webcrypto | 1019 } // namespace webcrypto |
850 | 1020 |
851 } // namespace content | 1021 } // namespace content |
OLD | NEW |