Chromium Code Reviews| 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/platform_crypto.h" | 5 #include "content/child/webcrypto/platform_crypto.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 #include <openssl/aes.h> | 8 #include <openssl/aes.h> |
| 9 #include <openssl/evp.h> | 9 #include <openssl/evp.h> |
| 10 #include <openssl/hmac.h> | 10 #include <openssl/hmac.h> |
| 11 #include <openssl/pkcs12.h> | |
| 11 #include <openssl/rand.h> | 12 #include <openssl/rand.h> |
| 12 #include <openssl/sha.h> | 13 #include <openssl/sha.h> |
| 13 | 14 |
| 14 #include "base/logging.h" | 15 #include "base/logging.h" |
| 15 #include "base/memory/scoped_ptr.h" | 16 #include "base/memory/scoped_ptr.h" |
| 16 #include "content/child/webcrypto/crypto_data.h" | 17 #include "content/child/webcrypto/crypto_data.h" |
| 17 #include "content/child/webcrypto/status.h" | 18 #include "content/child/webcrypto/status.h" |
| 18 #include "content/child/webcrypto/webcrypto_util.h" | 19 #include "content/child/webcrypto/webcrypto_util.h" |
| 19 #include "crypto/openssl_util.h" | 20 #include "crypto/openssl_util.h" |
| 20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | 21 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 42 } | 43 } |
| 43 | 44 |
| 44 const std::vector<unsigned char>& key() const { return key_; } | 45 const std::vector<unsigned char>& key() const { return key_; } |
| 45 | 46 |
| 46 private: | 47 private: |
| 47 const std::vector<unsigned char> key_; | 48 const std::vector<unsigned char> key_; |
| 48 | 49 |
| 49 DISALLOW_COPY_AND_ASSIGN(SymKey); | 50 DISALLOW_COPY_AND_ASSIGN(SymKey); |
| 50 }; | 51 }; |
| 51 | 52 |
| 53 class PublicKey : public Key { | |
| 54 public: | |
| 55 // Takes ownership of |key|. | |
| 56 // TODO(eroman): use Pass() semantics. | |
| 57 static Status Create(EVP_PKEY* key, | |
| 58 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 59 scoped_ptr<PublicKey>* out) { | |
| 60 out->reset(new PublicKey(key)); | |
| 61 return ExportKeySpki(out->get(), &(*out)->serialized_key_); | |
| 62 } | |
| 63 | |
| 64 EVP_PKEY* key() { return key_.get(); } | |
| 65 | |
| 66 virtual SymKey* AsSymKey() OVERRIDE { return NULL; } | |
| 67 virtual PublicKey* AsPublicKey() OVERRIDE { return this; } | |
| 68 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } | |
| 69 | |
| 70 virtual bool ThreadSafeSerializeForClone( | |
| 71 blink::WebVector<uint8>* key_data) OVERRIDE { | |
| 72 key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); | |
| 73 return true; | |
| 74 } | |
| 75 | |
| 76 private: | |
| 77 explicit PublicKey(EVP_PKEY* key) : key_(key) {} | |
| 78 | |
| 79 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> key_; | |
| 80 std::vector<uint8> serialized_key_; | |
| 81 | |
| 82 DISALLOW_COPY_AND_ASSIGN(PublicKey); | |
| 83 }; | |
| 84 | |
| 85 class PrivateKey : public Key { | |
| 86 public: | |
| 87 // Takes ownership of |key|. | |
| 88 // TODO(eroman): use Pass() semantics. | |
| 89 static Status Create(EVP_PKEY* key, | |
| 90 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 91 scoped_ptr<PrivateKey>* out) { | |
| 92 out->reset(new PrivateKey(key)); | |
| 93 return ExportKeyPkcs8(out->get(), algorithm, &(*out)->serialized_key_); | |
| 94 } | |
| 95 | |
| 96 EVP_PKEY* key() { return key_.get(); } | |
| 97 | |
| 98 virtual SymKey* AsSymKey() OVERRIDE { return NULL; } | |
| 99 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } | |
| 100 virtual PrivateKey* AsPrivateKey() OVERRIDE { return this; } | |
| 101 | |
| 102 virtual bool ThreadSafeSerializeForClone( | |
| 103 blink::WebVector<uint8>* key_data) OVERRIDE { | |
| 104 key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); | |
| 105 return true; | |
| 106 } | |
| 107 | |
| 108 private: | |
| 109 explicit PrivateKey(EVP_PKEY* key) : key_(key) {} | |
| 110 | |
| 111 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> key_; | |
| 112 std::vector<uint8> serialized_key_; | |
| 113 | |
| 114 DISALLOW_COPY_AND_ASSIGN(PrivateKey); | |
| 115 }; | |
| 116 | |
| 52 namespace { | 117 namespace { |
| 53 | 118 |
| 54 const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { | 119 const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { |
| 55 // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits | 120 // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits |
| 56 switch (key_length_bytes) { | 121 switch (key_length_bytes) { |
| 57 case 16: | 122 case 16: |
| 58 return EVP_aes_128_cbc(); | 123 return EVP_aes_128_cbc(); |
| 59 case 24: | 124 case 24: |
| 60 return EVP_aes_192_cbc(); | 125 return EVP_aes_192_cbc(); |
| 61 case 32: | 126 case 32: |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 146 const unsigned int final_output_len = | 211 const unsigned int final_output_len = |
| 147 static_cast<unsigned int>(output_len) + | 212 static_cast<unsigned int>(output_len) + |
| 148 static_cast<unsigned int>(final_output_chunk_len); | 213 static_cast<unsigned int>(final_output_chunk_len); |
| 149 DCHECK_LE(final_output_len, output_max_len); | 214 DCHECK_LE(final_output_len, output_max_len); |
| 150 | 215 |
| 151 buffer->resize(final_output_len); | 216 buffer->resize(final_output_len); |
| 152 | 217 |
| 153 return Status::Success(); | 218 return Status::Success(); |
| 154 } | 219 } |
| 155 | 220 |
| 221 // Creates a blink::WebCryptoAlgorithm having the modulus length and public | |
| 222 // exponent of |key|. | |
| 223 Status CreateRsaHashedKeyAlgorithm( | |
| 224 blink::WebCryptoAlgorithmId rsa_algorithm, | |
| 225 blink::WebCryptoAlgorithmId hash_algorithm, | |
| 226 EVP_PKEY* key, | |
| 227 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 228 DCHECK(IsAlgorithmRsa(rsa_algorithm)); | |
| 229 DCHECK(EVP_PKEY_id(key) == EVP_PKEY_RSA); | |
|
Ryan Sleevi
2014/06/27 01:43:25
DCHECK_EQ
eroman
2014/06/27 02:12:47
Done.
| |
| 230 | |
| 231 crypto::ScopedOpenSSL<RSA, RSA_free> rsa(EVP_PKEY_get1_RSA(key)); | |
| 232 if (!rsa.get()) | |
| 233 return Status::ErrorUnexpected(); | |
| 234 | |
| 235 unsigned int modulus_length_bits = RSA_size(rsa.get()) * 8; | |
| 236 | |
| 237 // Convert the public exponent to big-endian representation. | |
| 238 std::vector<uint8> e(BN_num_bytes(rsa.get()->e)); | |
| 239 if (e.size() == 0) | |
| 240 return Status::ErrorUnexpected(); | |
| 241 if (static_cast<int>(e.size()) != BN_bn2bin(rsa.get()->e, &e[0])) | |
|
Ryan Sleevi
2014/06/27 01:43:25
I think these may differ dependent on leading bits
eroman
2014/06/27 02:12:47
Looks like at least in the current implementation,
| |
| 242 return Status::ErrorUnexpected(); | |
| 243 | |
| 244 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( | |
| 245 rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); | |
| 246 | |
| 247 return Status::Success(); | |
| 248 } | |
| 249 | |
| 250 // Verifies that |key| is consistent with the input algorithm id, and creates a | |
| 251 // blink::WebCryptoKeyAlgorithm describing the key. | |
| 252 // Returns Status::Success() on success and sets |*key_algorithm|. | |
| 253 Status ValidateKeyTypeAndCreateKeyAlgorithm( | |
| 254 const blink::WebCryptoAlgorithm& algorithm, | |
| 255 EVP_PKEY* key, | |
| 256 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 257 if (IsAlgorithmRsa(algorithm.id())) { | |
| 258 if (EVP_PKEY_id(key) != EVP_PKEY_RSA) | |
| 259 return Status::DataError(); // Data did not define an RSA key. | |
| 260 return CreateRsaHashedKeyAlgorithm(algorithm.id(), | |
| 261 GetInnerHashAlgorithm(algorithm).id(), | |
| 262 key, | |
| 263 key_algorithm); | |
| 264 return Status::Success(); | |
| 265 } | |
| 266 | |
| 267 return Status::ErrorUnsupported(); | |
| 268 } | |
| 269 | |
| 156 } // namespace | 270 } // namespace |
| 157 | 271 |
| 158 class DigestorOpenSSL : public blink::WebCryptoDigestor { | 272 class DigestorOpenSSL : public blink::WebCryptoDigestor { |
| 159 public: | 273 public: |
| 160 explicit DigestorOpenSSL(blink::WebCryptoAlgorithmId algorithm_id) | 274 explicit DigestorOpenSSL(blink::WebCryptoAlgorithmId algorithm_id) |
| 161 : initialized_(false), | 275 : initialized_(false), |
| 162 digest_context_(EVP_MD_CTX_create()), | 276 digest_context_(EVP_MD_CTX_create()), |
| 163 algorithm_id_(algorithm_id) {} | 277 algorithm_id_(algorithm_id) {} |
| 164 | 278 |
| 165 virtual bool consume(const unsigned char* data, unsigned int size) { | 279 virtual bool consume(const unsigned char* data, unsigned int size) { |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 442 bool* signature_match) { | 556 bool* signature_match) { |
| 443 // TODO(eroman): http://crbug.com/267888 | 557 // TODO(eroman): http://crbug.com/267888 |
| 444 return Status::ErrorUnsupported(); | 558 return Status::ErrorUnsupported(); |
| 445 } | 559 } |
| 446 | 560 |
| 447 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, | 561 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, |
| 448 const CryptoData& key_data, | 562 const CryptoData& key_data, |
| 449 bool extractable, | 563 bool extractable, |
| 450 blink::WebCryptoKeyUsageMask usage_mask, | 564 blink::WebCryptoKeyUsageMask usage_mask, |
| 451 blink::WebCryptoKey* key) { | 565 blink::WebCryptoKey* key) { |
| 452 // TODO(eroman): http://crbug.com/267888 | 566 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 453 return Status::ErrorUnsupported(); | 567 |
| 568 crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf( | |
| 569 const_cast<uint8*>(key_data.bytes()), key_data.byte_length())); | |
| 570 if (!bio.get()) | |
| 571 return Status::ErrorUnexpected(); | |
| 572 | |
| 573 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> public_key( | |
| 574 d2i_PUBKEY_bio(bio.get(), NULL)); | |
| 575 if (!public_key.get()) | |
| 576 return Status::DataError(); | |
| 577 | |
| 578 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 579 Status status = ValidateKeyTypeAndCreateKeyAlgorithm( | |
| 580 algorithm, public_key.get(), &key_algorithm); | |
| 581 if (status.IsError()) | |
| 582 return status; | |
| 583 | |
| 584 scoped_ptr<PublicKey> key_handle; | |
| 585 status = PublicKey::Create(public_key.release(), key_algorithm, &key_handle); | |
| 586 if (status.IsError()) | |
| 587 return status; | |
| 588 | |
| 589 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 590 blink::WebCryptoKeyTypePublic, | |
| 591 extractable, | |
| 592 key_algorithm, | |
| 593 usage_mask); | |
| 594 | |
| 595 return Status::Success(); | |
| 454 } | 596 } |
| 455 | 597 |
| 456 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, | 598 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, |
| 457 const CryptoData& key_data, | 599 const CryptoData& key_data, |
| 458 bool extractable, | 600 bool extractable, |
| 459 blink::WebCryptoKeyUsageMask usage_mask, | 601 blink::WebCryptoKeyUsageMask usage_mask, |
| 460 blink::WebCryptoKey* key) { | 602 blink::WebCryptoKey* key) { |
| 461 // TODO(eroman): http://crbug.com/267888 | 603 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 462 return Status::ErrorUnsupported(); | 604 |
| 605 crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf( | |
| 606 const_cast<uint8*>(key_data.bytes()), key_data.byte_length())); | |
| 607 if (!bio.get()) | |
| 608 return Status::ErrorUnexpected(); | |
| 609 | |
| 610 crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf( | |
|
Ryan Sleevi
2014/06/27 01:43:24
// TODO reminder - need to validate the EVP data a
eroman
2014/06/27 02:12:47
Done. I have added comments linking to the corresp
| |
| 611 d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); | |
| 612 if (!p8inf.get()) | |
| 613 return Status::DataError(); | |
| 614 | |
| 615 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> private_key( | |
| 616 EVP_PKCS82PKEY(p8inf.get())); | |
| 617 if (!private_key.get()) | |
| 618 return Status::DataError(); | |
| 619 | |
| 620 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 621 Status status = ValidateKeyTypeAndCreateKeyAlgorithm( | |
| 622 algorithm, private_key.get(), &key_algorithm); | |
| 623 if (status.IsError()) | |
| 624 return status; | |
| 625 | |
| 626 scoped_ptr<PrivateKey> key_handle; | |
| 627 status = | |
| 628 PrivateKey::Create(private_key.release(), key_algorithm, &key_handle); | |
| 629 if (status.IsError()) | |
| 630 return status; | |
| 631 | |
| 632 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 633 blink::WebCryptoKeyTypePrivate, | |
| 634 extractable, | |
| 635 key_algorithm, | |
| 636 usage_mask); | |
| 637 | |
| 638 return Status::Success(); | |
| 463 } | 639 } |
| 464 | 640 |
| 465 Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) { | 641 Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) { |
| 466 // TODO(eroman): http://crbug.com/267888 | 642 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 467 return Status::ErrorUnsupported(); | 643 crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem())); |
| 644 if (!i2d_PUBKEY_bio(bio.get(), key->key())) | |
| 645 return Status::ErrorUnexpected(); | |
| 646 | |
| 647 uint8* data = NULL; | |
| 648 long len = BIO_get_mem_data(bio.get(), &data); | |
| 649 if (!data || len < 0) | |
| 650 return Status::ErrorUnexpected(); | |
| 651 | |
| 652 buffer->assign(data, data + len); | |
| 653 return Status::Success(); | |
| 468 } | 654 } |
| 469 | 655 |
| 470 Status ExportKeyPkcs8(PrivateKey* key, | 656 Status ExportKeyPkcs8(PrivateKey* key, |
| 471 const blink::WebCryptoKeyAlgorithm& key_algorithm, | 657 const blink::WebCryptoKeyAlgorithm& key_algorithm, |
| 472 std::vector<uint8>* buffer) { | 658 std::vector<uint8>* buffer) { |
| 473 // TODO(eroman): http://crbug.com/267888 | 659 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 474 return Status::ErrorUnsupported(); | 660 crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem())); |
| 661 | |
| 662 if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key->key())) | |
| 663 return Status::ErrorUnexpected(); | |
| 664 | |
| 665 uint8* data = NULL; | |
| 666 long len = BIO_get_mem_data(bio.get(), &data); | |
| 667 if (!data || len < 0) | |
| 668 return Status::ErrorUnexpected(); | |
| 669 | |
| 670 buffer->assign(data, data + len); | |
| 671 return Status::Success(); | |
| 475 } | 672 } |
| 476 | 673 |
| 477 Status ExportRsaPublicKey(PublicKey* key, | 674 Status ExportRsaPublicKey(PublicKey* key, |
| 478 std::vector<uint8>* modulus, | 675 std::vector<uint8>* modulus, |
| 479 std::vector<uint8>* public_exponent) { | 676 std::vector<uint8>* public_exponent) { |
| 480 // TODO(eroman): http://crbug.com/267888 | 677 // TODO(eroman): http://crbug.com/267888 |
| 481 return Status::ErrorUnsupported(); | 678 return Status::ErrorUnsupported(); |
| 482 } | 679 } |
| 483 | 680 |
| 484 Status ExportRsaPrivateKey(PrivateKey* key, | 681 Status ExportRsaPrivateKey(PrivateKey* key, |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 511 blink::WebCryptoKey* key) { | 708 blink::WebCryptoKey* key) { |
| 512 // TODO(eroman): http://crbug.com/267888 | 709 // TODO(eroman): http://crbug.com/267888 |
| 513 return false; | 710 return false; |
| 514 } | 711 } |
| 515 | 712 |
| 516 } // namespace platform | 713 } // namespace platform |
| 517 | 714 |
| 518 } // namespace webcrypto | 715 } // namespace webcrypto |
| 519 | 716 |
| 520 } // namespace content | 717 } // namespace content |
| OLD | NEW |