diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-13 13:24:50 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-14 10:57:25 +0000 |
commit | af3d4809763ef308f08ced947a73b624729ac7ea (patch) | |
tree | 4402b911e30383f6c6dace1e8cf3b8e85355db3a /chromium/components/webcrypto | |
parent | 0e8ff63a407fe323e215bb1a2c423c09a4747c8a (diff) | |
download | qtwebengine-chromium-af3d4809763ef308f08ced947a73b624729ac7ea.tar.gz |
BASELINE: Update Chromium to 47.0.2526.14
Also adding in sources needed for spellchecking.
Change-Id: Idd44170fa1616f26315188970a8d5ba7d472b18a
Reviewed-by: Michael BrĂ¼ning <michael.bruning@theqtcompany.com>
Diffstat (limited to 'chromium/components/webcrypto')
79 files changed, 7072 insertions, 4700 deletions
diff --git a/chromium/components/webcrypto/BUILD.gn b/chromium/components/webcrypto/BUILD.gn index f14aed5e4d5..aebf172f3c4 100644 --- a/chromium/components/webcrypto/BUILD.gn +++ b/chromium/components/webcrypto/BUILD.gn @@ -11,21 +11,48 @@ source_set("webcrypto") { "algorithm_dispatch.h", "algorithm_implementation.cc", "algorithm_implementation.h", + "algorithm_implementations.h", "algorithm_registry.cc", "algorithm_registry.h", + "algorithms/aes.cc", + "algorithms/aes.h", + "algorithms/aes_cbc.cc", + "algorithms/aes_ctr.cc", + "algorithms/aes_gcm.cc", + "algorithms/aes_kw.cc", + "algorithms/asymmetric_key_util.cc", + "algorithms/asymmetric_key_util.h", + "algorithms/ec.cc", + "algorithms/ec.h", + "algorithms/ecdh.cc", + "algorithms/ecdsa.cc", + "algorithms/hkdf.cc", + "algorithms/hmac.cc", + "algorithms/pbkdf2.cc", + "algorithms/rsa.cc", + "algorithms/rsa.h", + "algorithms/rsa_oaep.cc", + "algorithms/rsa_pss.cc", + "algorithms/rsa_sign.cc", + "algorithms/rsa_sign.h", + "algorithms/rsa_ssa.cc", + "algorithms/secret_key_util.cc", + "algorithms/secret_key_util.h", + "algorithms/sha.cc", + "algorithms/util.cc", + "algorithms/util.h", + "blink_key_handle.cc", + "blink_key_handle.h", "crypto_data.cc", "crypto_data.h", "generate_key_result.cc", "generate_key_result.h", "jwk.cc", "jwk.h", - "platform_crypto.h", "status.cc", "status.h", "webcrypto_impl.cc", "webcrypto_impl.h", - "webcrypto_util.cc", - "webcrypto_util.h", ] deps = [ @@ -34,80 +61,31 @@ source_set("webcrypto") { "//crypto:platform", "//third_party/WebKit/public:blink", ] - - if (!use_openssl) { - sources += [ - "nss/aes_algorithm_nss.cc", - "nss/aes_algorithm_nss.h", - "nss/aes_cbc_nss.cc", - "nss/aes_gcm_nss.cc", - "nss/aes_kw_nss.cc", - "nss/hmac_nss.cc", - "nss/key_nss.cc", - "nss/key_nss.h", - "nss/rsa_hashed_algorithm_nss.cc", - "nss/rsa_hashed_algorithm_nss.h", - "nss/rsa_oaep_nss.cc", - "nss/rsa_ssa_nss.cc", - "nss/sha_nss.cc", - "nss/sym_key_nss.cc", - "nss/sym_key_nss.h", - "nss/util_nss.cc", - "nss/util_nss.h", - ] - } else { - sources += [ - "openssl/aes_algorithm_openssl.cc", - "openssl/aes_algorithm_openssl.h", - "openssl/aes_cbc_openssl.cc", - "openssl/aes_ctr_openssl.cc", - "openssl/aes_gcm_openssl.cc", - "openssl/aes_kw_openssl.cc", - "openssl/ec_algorithm_openssl.cc", - "openssl/ec_algorithm_openssl.h", - "openssl/ecdh_openssl.cc", - "openssl/ecdsa_openssl.cc", - "openssl/hkdf_openssl.cc", - "openssl/hmac_openssl.cc", - "openssl/key_openssl.cc", - "openssl/key_openssl.h", - "openssl/pbkdf2_openssl.cc", - "openssl/rsa_hashed_algorithm_openssl.cc", - "openssl/rsa_hashed_algorithm_openssl.h", - "openssl/rsa_oaep_openssl.cc", - "openssl/rsa_pss_openssl.cc", - "openssl/rsa_sign_openssl.cc", - "openssl/rsa_sign_openssl.h", - "openssl/rsa_ssa_openssl.cc", - "openssl/sha_openssl.cc", - "openssl/util_openssl.cc", - "openssl/util_openssl.h", - ] - } } source_set("unit_tests") { testonly = true sources = [ - "test/aes_cbc_unittest.cc", - "test/aes_ctr_unittest.cc", - "test/aes_gcm_unittest.cc", - "test/aes_kw_unittest.cc", - "test/ecdh_unittest.cc", - "test/ecdsa_unittest.cc", - "test/hmac_unittest.cc", - "test/rsa_oaep_unittest.cc", - "test/rsa_pss_unittest.cc", - "test/rsa_ssa_unittest.cc", - "test/sha_unittest.cc", - "test/status_unittest.cc", - "test/test_helpers.cc", - "test/test_helpers.h", + "algorithms/aes_cbc_unittest.cc", + "algorithms/aes_ctr_unittest.cc", + "algorithms/aes_gcm_unittest.cc", + "algorithms/aes_kw_unittest.cc", + "algorithms/ecdh_unittest.cc", + "algorithms/ecdsa_unittest.cc", + "algorithms/hmac_unittest.cc", + "algorithms/rsa_oaep_unittest.cc", + "algorithms/rsa_pss_unittest.cc", + "algorithms/rsa_ssa_unittest.cc", + "algorithms/sha_unittest.cc", + "algorithms/test_helpers.cc", + "algorithms/test_helpers.h", + "status_unittest.cc", ] deps = [ ":webcrypto", "//base/test:test_support", + "//components/test_runner:test_runner", "//crypto", "//crypto:platform", "//testing/perf", diff --git a/chromium/components/webcrypto/DEPS b/chromium/components/webcrypto/DEPS index 5cdc8e79e93..14ca3e02e19 100644 --- a/chromium/components/webcrypto/DEPS +++ b/chromium/components/webcrypto/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/test_runner", "+crypto", "+third_party/re2", "+third_party/WebKit/public/platform", diff --git a/chromium/components/webcrypto/algorithm_dispatch.cc b/chromium/components/webcrypto/algorithm_dispatch.cc index ce29c91a3a6..63f529b0b00 100644 --- a/chromium/components/webcrypto/algorithm_dispatch.cc +++ b/chromium/components/webcrypto/algorithm_dispatch.cc @@ -6,12 +6,12 @@ #include "base/logging.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithm_implementations.h" #include "components/webcrypto/algorithm_registry.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/platform_crypto.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" +#include "crypto/openssl_util.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" namespace webcrypto { @@ -65,7 +65,7 @@ Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8_t>* buffer) { - if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) + if (!key.keyUsageAllows(blink::WebCryptoKeyUsageEncrypt)) return Status::ErrorUnexpected(); return EncryptDontCheckUsage(algorithm, key, data, buffer); } @@ -74,7 +74,7 @@ Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8_t>* buffer) { - if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt)) + if (!key.keyUsageAllows(blink::WebCryptoKeyUsageDecrypt)) return Status::ErrorUnexpected(); return DecryptDontCheckKeyUsage(algorithm, key, data, buffer); } @@ -150,7 +150,7 @@ Status Sign(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8_t>* buffer) { - if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign)) + if (!key.keyUsageAllows(blink::WebCryptoKeyUsageSign)) return Status::ErrorUnexpected(); if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); @@ -168,7 +168,7 @@ Status Verify(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& signature, const CryptoData& data, bool* signature_match) { - if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify)) + if (!key.keyUsageAllows(blink::WebCryptoKeyUsageVerify)) return Status::ErrorUnexpected(); if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); @@ -186,7 +186,7 @@ Status WrapKey(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoAlgorithm& wrapping_algorithm, std::vector<uint8_t>* buffer) { - if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey)) + if (!wrapping_key.keyUsageAllows(blink::WebCryptoKeyUsageWrapKey)) return Status::ErrorUnexpected(); std::vector<uint8_t> exported_data; @@ -205,7 +205,7 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format, bool extractable, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) { - if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) + if (!wrapping_key.keyUsageAllows(blink::WebCryptoKeyUsageUnwrapKey)) return Status::ErrorUnexpected(); if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) return Status::ErrorUnexpected(); @@ -230,7 +230,7 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format, // information about the plaintext of the encrypted key (for instance the JWK // key_ops). As long as the ImportKey error messages don't describe actual // key bytes however this should be OK. For more discussion see - // http://crubg.com/372040 + // http://crbug.com/372040 return ImportKey(format, CryptoData(buffer), algorithm, extractable, usages, key); } @@ -239,7 +239,7 @@ Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& base_key, unsigned int length_bits, std::vector<uint8_t>* derived_bytes) { - if (!KeyUsageAllows(base_key, blink::WebCryptoKeyUsageDeriveBits)) + if (!base_key.keyUsageAllows(blink::WebCryptoKeyUsageDeriveBits)) return Status::ErrorUnexpected(); if (algorithm.id() != base_key.algorithm().id()) @@ -261,7 +261,7 @@ Status DeriveKey(const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* derived_key) { - if (!KeyUsageAllows(base_key, blink::WebCryptoKeyUsageDeriveKey)) + if (!base_key.keyUsageAllows(blink::WebCryptoKeyUsageDeriveKey)) return Status::ErrorUnexpected(); if (algorithm.id() != base_key.algorithm().id()) @@ -309,8 +309,8 @@ Status DeriveKey(const blink::WebCryptoAlgorithm& algorithm, scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( blink::WebCryptoAlgorithmId algorithm) { - PlatformInit(); - return CreatePlatformDigestor(algorithm); + crypto::EnsureOpenSSLInit(); + return CreateDigestorImplementation(algorithm); } bool SerializeKeyForClone(const blink::WebCryptoKey& key, diff --git a/chromium/components/webcrypto/algorithm_implementation.cc b/chromium/components/webcrypto/algorithm_implementation.cc index 1d98b4427ba..8db7482ce73 100644 --- a/chromium/components/webcrypto/algorithm_implementation.cc +++ b/chromium/components/webcrypto/algorithm_implementation.cc @@ -4,6 +4,7 @@ #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/status.h" namespace webcrypto { @@ -181,7 +182,8 @@ Status AlgorithmImplementation::ExportKeyJwk( Status AlgorithmImplementation::SerializeKeyForClone( const blink::WebCryptoKey& key, blink::WebVector<uint8_t>* key_data) const { - return Status::ErrorUnsupported(); + *key_data = GetSerializedKeyData(key); + return Status::Success(); } Status AlgorithmImplementation::DeserializeKeyForClone( diff --git a/chromium/components/webcrypto/algorithm_implementation.h b/chromium/components/webcrypto/algorithm_implementation.h index bbe42146dff..993c168d5f7 100644 --- a/chromium/components/webcrypto/algorithm_implementation.h +++ b/chromium/components/webcrypto/algorithm_implementation.h @@ -36,6 +36,10 @@ class Status; // * The key usages have already been verified. In fact in the case of calls // to Encrypt()/Decrypt() the corresponding key usages may not be present // (when wrapping/unwrapping). +// +// An AlgorithmImplementation can also assume that +// crypto::EnsureOpenSSLInit() will be called before any of its +// methods are invoked (except the constructor). class AlgorithmImplementation { public: virtual ~AlgorithmImplementation(); @@ -204,9 +208,11 @@ class AlgorithmImplementation { // // Tests to verify structured cloning are available in: // LayoutTests/crypto/clone-*.html - virtual Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const; + + // Note that SerializeKeyForClone() is not virtual because all + // implementations end up doing the same thing. + Status SerializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<uint8_t>* key_data) const; virtual Status DeserializeKeyForClone( const blink::WebCryptoKeyAlgorithm& algorithm, diff --git a/chromium/components/webcrypto/algorithm_implementations.h b/chromium/components/webcrypto/algorithm_implementations.h new file mode 100644 index 00000000000..a6bea01f63f --- /dev/null +++ b/chromium/components/webcrypto/algorithm_implementations.h @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHM_IMPLEMENTATIONS_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHM_IMPLEMENTATIONS_H_ + +#include "base/memory/scoped_ptr.h" +#include "third_party/WebKit/public/platform/WebCrypto.h" + +// The definitions for these functions live in the algorithms/ directory. +namespace webcrypto { + +class AlgorithmImplementation; + +scoped_ptr<blink::WebCryptoDigestor> CreateDigestorImplementation( + blink::WebCryptoAlgorithmId algorithm); + +scoped_ptr<AlgorithmImplementation> CreateShaImplementation(); +scoped_ptr<AlgorithmImplementation> CreateAesCbcImplementation(); +scoped_ptr<AlgorithmImplementation> CreateAesCtrImplementation(); +scoped_ptr<AlgorithmImplementation> CreateAesGcmImplementation(); +scoped_ptr<AlgorithmImplementation> CreateAesKwImplementation(); +scoped_ptr<AlgorithmImplementation> CreateHmacImplementation(); +scoped_ptr<AlgorithmImplementation> CreateRsaOaepImplementation(); +scoped_ptr<AlgorithmImplementation> CreateRsaSsaImplementation(); +scoped_ptr<AlgorithmImplementation> CreateRsaPssImplementation(); +scoped_ptr<AlgorithmImplementation> CreateEcdsaImplementation(); +scoped_ptr<AlgorithmImplementation> CreateEcdhImplementation(); +scoped_ptr<AlgorithmImplementation> CreateHkdfImplementation(); +scoped_ptr<AlgorithmImplementation> CreatePbkdf2Implementation(); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_ALGORITHM_IMPLEMENTATIONS_H_ diff --git a/chromium/components/webcrypto/algorithm_registry.cc b/chromium/components/webcrypto/algorithm_registry.cc index ea4799e70e9..b82737e732b 100644 --- a/chromium/components/webcrypto/algorithm_registry.cc +++ b/chromium/components/webcrypto/algorithm_registry.cc @@ -6,8 +6,9 @@ #include "base/lazy_instance.h" #include "components/webcrypto/algorithm_implementation.h" -#include "components/webcrypto/platform_crypto.h" +#include "components/webcrypto/algorithm_implementations.h" #include "components/webcrypto/status.h" +#include "crypto/openssl_util.h" namespace webcrypto { @@ -17,20 +18,20 @@ namespace { class AlgorithmRegistry { public: AlgorithmRegistry() - : sha_(CreatePlatformShaImplementation()), - aes_gcm_(CreatePlatformAesGcmImplementation()), - aes_cbc_(CreatePlatformAesCbcImplementation()), - aes_ctr_(CreatePlatformAesCtrImplementation()), - aes_kw_(CreatePlatformAesKwImplementation()), - hmac_(CreatePlatformHmacImplementation()), - rsa_ssa_(CreatePlatformRsaSsaImplementation()), - rsa_oaep_(CreatePlatformRsaOaepImplementation()), - rsa_pss_(CreatePlatformRsaPssImplementation()), - ecdsa_(CreatePlatformEcdsaImplementation()), - ecdh_(CreatePlatformEcdhImplementation()), - hkdf_(CreatePlatformHkdfImplementation()), - pbkdf2_(CreatePlatformPbkdf2Implementation()) { - PlatformInit(); + : sha_(CreateShaImplementation()), + aes_gcm_(CreateAesGcmImplementation()), + aes_cbc_(CreateAesCbcImplementation()), + aes_ctr_(CreateAesCtrImplementation()), + aes_kw_(CreateAesKwImplementation()), + hmac_(CreateHmacImplementation()), + rsa_ssa_(CreateRsaSsaImplementation()), + rsa_oaep_(CreateRsaOaepImplementation()), + rsa_pss_(CreateRsaPssImplementation()), + ecdsa_(CreateEcdsaImplementation()), + ecdh_(CreateEcdhImplementation()), + hkdf_(CreateHkdfImplementation()), + pbkdf2_(CreatePbkdf2Implementation()) { + crypto::EnsureOpenSSLInit(); } const AlgorithmImplementation* GetAlgorithm( diff --git a/chromium/components/webcrypto/openssl/aes_algorithm_openssl.cc b/chromium/components/webcrypto/algorithms/aes.cc index 05e30c5bfd5..09dbfd45c49 100644 --- a/chromium/components/webcrypto/openssl/aes_algorithm_openssl.cc +++ b/chromium/components/webcrypto/algorithms/aes.cc @@ -2,19 +2,44 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/aes_algorithm_openssl.h" +#include "components/webcrypto/algorithms/aes.h" #include "base/logging.h" +#include "components/webcrypto/algorithms/secret_key_util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/jwk.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" namespace webcrypto { +namespace { + +// Creates an AES algorithm name for the given key size (in bytes). For +// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. +std::string MakeJwkAesAlgorithmName(const std::string& suffix, + size_t keylen_bytes) { + if (keylen_bytes == 16) + return std::string("A128") + suffix; + if (keylen_bytes == 24) + return std::string("A192") + suffix; + if (keylen_bytes == 32) + return std::string("A256") + suffix; + return std::string(); +} + +// Synthesizes an import algorithm given a key algorithm, so that +// deserialization can re-use the ImportKey*() methods. +blink::WebCryptoAlgorithm SynthesizeImportAlgorithmForClone( + const blink::WebCryptoKeyAlgorithm& algorithm) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate(algorithm.id(), + nullptr); +} + +} // namespace + AesAlgorithm::AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages, const std::string& jwk_suffix) : all_key_usages_(all_key_usages), jwk_suffix_(jwk_suffix) { @@ -32,14 +57,18 @@ Status AesAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usages, GenerateKeyResult* result) const { - Status status = CheckKeyCreationUsages(all_key_usages_, usages, false); + Status status = CheckSecretKeyCreationUsages(all_key_usages_, usages); if (status.IsError()) return status; - unsigned int keylen_bits; - status = GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); - if (status.IsError()) - return status; + unsigned int keylen_bits = algorithm.aesKeyGenParams()->lengthBits(); + + // 192-bit AES is intentionally unsupported (http://crbug.com/533699). + if (keylen_bits == 192) + return Status::ErrorAes192BitUnsupported(); + + if (keylen_bits != 128 && keylen_bits != 256) + return Status::ErrorGenerateAesKeyLength(); return GenerateWebCryptoSecretKey( blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), @@ -52,7 +81,7 @@ Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( switch (format) { case blink::WebCryptoKeyFormatRaw: case blink::WebCryptoKeyFormatJwk: - return CheckKeyCreationUsages(all_key_usages_, usages, false); + return CheckSecretKeyCreationUsages(all_key_usages_, usages); default: return Status::ErrorUnsupportedImportKeyFormat(); } @@ -64,9 +93,13 @@ Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const { const unsigned int keylen_bytes = key_data.byte_length(); - Status status = VerifyAesKeyLengthForImport(keylen_bytes); - if (status.IsError()) - return status; + + // 192-bit AES is intentionally unsupported (http://crbug.com/533699). + if (keylen_bytes == 24) + return Status::ErrorAes192BitUnsupported(); + + if (keylen_bytes != 16 && keylen_bytes != 32) + return Status::ErrorImportAesKeyLength(); // No possibility of overflow. unsigned int keylen_bits = keylen_bytes * 8; @@ -83,25 +116,46 @@ Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const { std::vector<uint8_t> raw_data; - Status status = ReadAesSecretKeyJwk(key_data, jwk_suffix_, extractable, - usages, &raw_data); + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages, + &raw_data, &jwk); + if (status.IsError()) + return status; + + bool has_jwk_alg; + std::string jwk_alg; + status = jwk.GetAlg(&jwk_alg, &has_jwk_alg); if (status.IsError()) return status; + if (has_jwk_alg) { + std::string expected_algorithm_name = + MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()); + + if (jwk_alg != expected_algorithm_name) { + // Give a different error message if the key length was wrong. + if (jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 16) || + jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 24) || + jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 32)) { + return Status::ErrorJwkIncorrectKeyLength(); + } + return Status::ErrorJwkAlgorithmInconsistent(); + } + } + return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, key); } Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { - *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + *buffer = GetSymmetricKeyData(key); return Status::Success(); } Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { - const std::vector<uint8_t>& raw_data = - SymKeyOpenSsl::Cast(key)->raw_key_data(); + const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key); WriteSecretKeyJwk(CryptoData(raw_data), MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()), @@ -110,13 +164,6 @@ Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, return Status::Success(); } -Status AesAlgorithm::SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const { - key_data->assign(SymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); -} - Status AesAlgorithm::DeserializeKeyForClone( const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, @@ -124,15 +171,25 @@ Status AesAlgorithm::DeserializeKeyForClone( blink::WebCryptoKeyUsageMask usages, const CryptoData& key_data, blink::WebCryptoKey* key) const { - return ImportKeyRaw(key_data, CreateAlgorithm(algorithm.id()), extractable, - usages, key); + return ImportKeyRaw(key_data, SynthesizeImportAlgorithmForClone(algorithm), + extractable, usages, key); } Status AesAlgorithm::GetKeyLength( const blink::WebCryptoAlgorithm& key_length_algorithm, bool* has_length_bits, unsigned int* length_bits) const { - return GetAesKeyLength(key_length_algorithm, has_length_bits, length_bits); + *has_length_bits = true; + *length_bits = key_length_algorithm.aesDerivedKeyParams()->lengthBits(); + + if (*length_bits == 128 || *length_bits == 256) + return Status::Success(); + + // 192-bit AES is intentionally unsupported (http://crbug.com/533699). + if (*length_bits == 192) + return Status::ErrorAes192BitUnsupported(); + + return Status::ErrorGetAesKeyLength(); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/aes_algorithm_openssl.h b/chromium/components/webcrypto/algorithms/aes.h index fd87bacd0ec..62cc3b1e406 100644 --- a/chromium/components/webcrypto/openssl/aes_algorithm_openssl.h +++ b/chromium/components/webcrypto/algorithms/aes.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_AES_ALGORITHM_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_AES_ALGORITHM_OPENSSL_H_ +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_AES_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_AES_H_ #include "components/webcrypto/algorithm_implementation.h" @@ -53,10 +53,6 @@ class AesAlgorithm : public AlgorithmImplementation { Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const override; - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override; - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -75,4 +71,4 @@ class AesAlgorithm : public AlgorithmImplementation { } // namespace webcrypto -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_AES_ALGORITHM_OPENSSL_H_ +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_AES_H_ diff --git a/chromium/components/webcrypto/openssl/aes_cbc_openssl.cc b/chromium/components/webcrypto/algorithms/aes_cbc.cc index 4d1ece247df..0d9c88ef6f9 100644 --- a/chromium/components/webcrypto/openssl/aes_cbc_openssl.cc +++ b/chromium/components/webcrypto/algorithms/aes_cbc.cc @@ -8,12 +8,11 @@ #include "base/logging.h" #include "base/numerics/safe_math.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/aes.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/aes_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -23,7 +22,7 @@ namespace webcrypto { namespace { const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) { - // BoringSSL does not support 192-bit AES keys. + // 192-bit AES is intentionally unsupported (http://crbug.com/533699). switch (key_length_bytes) { case 16: return EVP_aes_128_cbc(); @@ -42,8 +41,7 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation, crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); - const std::vector<uint8_t>& raw_key = - SymKeyOpenSsl::Cast(key)->raw_key_data(); + const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key); if (params->iv().size() != 16) return Status::ErrorIncorrectSizeAesCbcIv(); @@ -122,8 +120,8 @@ class AesCbcImplementation : public AesAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformAesCbcImplementation() { - return new AesCbcImplementation; +scoped_ptr<AlgorithmImplementation> CreateAesCbcImplementation() { + return make_scoped_ptr(new AesCbcImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc new file mode 100644 index 00000000000..44f07ea0bce --- /dev/null +++ b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc @@ -0,0 +1,568 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Creates an AES-CBC algorithm. +blink::WebCryptoAlgorithm CreateAesCbcAlgorithm( + const std::vector<uint8_t>& iv) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdAesCbc, + new blink::WebCryptoAesCbcParams(vector_as_array(&iv), + static_cast<unsigned int>(iv.size()))); +} + +blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm( + unsigned short key_length_bits) { + return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesCbc, + key_length_bits); +} + +blink::WebCryptoKey GetTestAesCbcKey() { + const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c"; + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + HexStringToBytes(key_hex), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + // Verify exported raw key is identical to the imported data + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_BYTES_EQ_HEX(key_hex, raw_key); + return key; +} + +class WebCryptoAesCbcTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoAesCbcTest, InputTooLarge) { + std::vector<uint8_t> output; + + std::vector<uint8_t> iv(16); + + // Give an input that is too large. It would cause integer overflow when + // narrowing the ciphertext size to an int, since OpenSSL operates on signed + // int lengths NOT unsigned. + // + // Pretend the input is large. Don't pass data pointer as NULL in case that + // is special cased; the implementation shouldn't actually dereference the + // data. + CryptoData input(&iv[0], INT_MAX - 3); + + EXPECT_EQ( + Status::ErrorDataTooLarge(), + Encrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output)); + EXPECT_EQ( + Status::ErrorDataTooLarge(), + Decrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output)); +} + +TEST_F(WebCryptoAesCbcTest, ExportKeyUnsupportedFormat) { + std::vector<uint8_t> output; + + // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret + // keys). + EXPECT_EQ( + Status::ErrorUnsupportedExportKeyFormat(), + ExportKey(blink::WebCryptoKeyFormatSpki, GetTestAesCbcKey(), &output)); + EXPECT_EQ( + Status::ErrorUnsupportedExportKeyFormat(), + ExportKey(blink::WebCryptoKeyFormatPkcs8, GetTestAesCbcKey(), &output)); +} + +// Tests importing of keys (in a variety of formats), errors during import, +// encryption, and decryption, using known answers. +TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); + std::vector<uint8_t> key_data = + GetKeyDataFromJsonTestCase(test, key_format); + std::string import_error = "Success"; + test->GetString("import_error", &import_error); + + // Import the key. + blink::WebCryptoKey key; + Status status = ImportKey( + key_format, CryptoData(key_data), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + &key); + ASSERT_EQ(import_error, StatusToString(status)); + if (status.IsError()) + continue; + + // Test encryption. + if (test->HasKey("plain_text")) { + std::vector<uint8_t> test_plain_text = + GetBytesFromHexString(test, "plain_text"); + + std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); + + std::string encrypt_error = "Success"; + test->GetString("encrypt_error", &encrypt_error); + + std::vector<uint8_t> output; + status = Encrypt(CreateAesCbcAlgorithm(test_iv), key, + CryptoData(test_plain_text), &output); + ASSERT_EQ(encrypt_error, StatusToString(status)); + if (status.IsError()) + continue; + + std::vector<uint8_t> test_cipher_text = + GetBytesFromHexString(test, "cipher_text"); + + EXPECT_BYTES_EQ(test_cipher_text, output); + } + + // Test decryption. + if (test->HasKey("cipher_text")) { + std::vector<uint8_t> test_cipher_text = + GetBytesFromHexString(test, "cipher_text"); + + std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); + + std::string decrypt_error = "Success"; + test->GetString("decrypt_error", &decrypt_error); + + std::vector<uint8_t> output; + status = Decrypt(CreateAesCbcAlgorithm(test_iv), key, + CryptoData(test_cipher_text), &output); + ASSERT_EQ(decrypt_error, StatusToString(status)); + if (status.IsError()) + continue; + + std::vector<uint8_t> test_plain_text = + GetBytesFromHexString(test, "plain_text"); + + EXPECT_BYTES_EQ(test_plain_text, output); + } + } +} + +// TODO(eroman): Do this same test for AES-GCM, AES-KW, AES-CTR ? +TEST_F(WebCryptoAesCbcTest, GenerateKeyIsRandom) { + // Check key generation for each allowed key length. + std::vector<blink::WebCryptoAlgorithm> algorithm; + const unsigned short kKeyLength[] = {128, 256}; + for (size_t key_length_i = 0; key_length_i < arraysize(kKeyLength); + ++key_length_i) { + blink::WebCryptoKey key; + + std::vector<std::vector<uint8_t>> keys; + std::vector<uint8_t> key_bytes; + + // Generate a small sample of keys. + for (int j = 0; j < 16; ++j) { + ASSERT_EQ(Status::Success(), + GenerateSecretKey( + CreateAesCbcKeyGenAlgorithm(kKeyLength[key_length_i]), true, + blink::WebCryptoKeyUsageEncrypt, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_bytes)); + EXPECT_EQ(key_bytes.size() * 8, + key.algorithm().aesParams()->lengthBits()); + keys.push_back(key_bytes); + } + // Ensure all entries in the key sample set are unique. This is a simplistic + // estimate of whether the generated keys appear random. + EXPECT_FALSE(CopiesExist(keys)); + } +} + +TEST_F(WebCryptoAesCbcTest, GenerateKeyBadLength) { + const unsigned short kKeyLen[] = {0, 127, 257}; + blink::WebCryptoKey key; + for (size_t i = 0; i < arraysize(kKeyLen); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(Status::ErrorGenerateAesKeyLength(), + GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true, + blink::WebCryptoKeyUsageEncrypt, &key)); + } +} + +TEST_F(WebCryptoAesCbcTest, ImportKeyEmptyUsage) { + blink::WebCryptoKey key; + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(16)), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + 0, &key)); +} + +// If key_ops is specified but empty, no key usages are allowed for the key. +TEST_F(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetBoolean("ext", false); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + dict.Set("key_ops", new base::ListValue); // Takes ownership. + + // The JWK does not contain encrypt usages. + EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); + + // The JWK does not contain sign usage (nor is it applicable). + EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageSign, &key)); +} + +// If key_ops is missing, then any key usages can be specified. +TEST_F(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages()); + + // The JWK does not contain sign usage (nor is it applicable). + EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageVerify, &key)); +} + +TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + base::ListValue* key_ops = new base::ListValue; + dict.Set("key_ops", key_ops); // Takes ownership. + + key_ops->AppendString("encrypt"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages()); + + key_ops->AppendString("decrypt"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageDecrypt, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt, key.usages()); + + EXPECT_EQ( + Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt, + &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + key.usages()); +} + +// Test failure if input usage is NOT a strict subset of the JWK usage. +TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + base::ListValue* key_ops = new base::ListValue; + dict.Set("key_ops", key_ops); // Takes ownership. + + key_ops->AppendString("encrypt"); + + EXPECT_EQ( + Status::ErrorJwkKeyopsInconsistent(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + &key)); +} + +TEST_F(WebCryptoAesCbcTest, ImportKeyJwkUseEnc) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + + // Test JWK composite use 'enc' usage + dict.SetString("alg", "A128CBC"); + dict.SetString("use", "enc"); + EXPECT_EQ( + Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | + blink::WebCryptoKeyUsageWrapKey | + blink::WebCryptoKeyUsageUnwrapKey, + &key)); + EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | + blink::WebCryptoKeyUsageWrapKey | + blink::WebCryptoKeyUsageUnwrapKey, + key.usages()); +} + +TEST_F(WebCryptoAesCbcTest, ImportJwkInvalidJson) { + blink::WebCryptoKey key; + // Fail on empty JSON. + EXPECT_EQ( + Status::ErrorJwkNotDictionary(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(MakeJsonVector("")), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); + + // Fail on invalid JSON. + const std::vector<uint8_t> bad_json_vec = MakeJsonVector( + "{" + "\"kty\" : \"oct\"," + "\"alg\" : \"HS256\"," + "\"use\" : "); + EXPECT_EQ(Status::ErrorJwkNotDictionary(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(bad_json_vec), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); +} + +// Fail on inconsistent key_ops - asking for "encrypt" however JWK contains +// only "foo". +TEST_F(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) { + blink::WebCryptoKey key; + + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + + base::ListValue* key_ops = new base::ListValue; + // Note: the following call makes dict assume ownership of key_ops. + dict.Set("key_ops", key_ops); + key_ops->AppendString("foo"); + EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), false, + blink::WebCryptoKeyUsageEncrypt, &key)); +} + +TEST_F(WebCryptoAesCbcTest, ImportExportJwk) { + const blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); + + // AES-CBC 128 + ImportExportJwkSymmetricKey( + 128, algorithm, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + "A128CBC"); + + // AES-CBC 256 + ImportExportJwkSymmetricKey(256, algorithm, blink::WebCryptoKeyUsageDecrypt, + "A256CBC"); + + // Large usage value + ImportExportJwkSymmetricKey( + 256, algorithm, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, + "A256CBC"); +} + +// 192-bit AES is intentionally unsupported (http://crbug.com/533699). +TEST_F(WebCryptoAesCbcTest, GenerateAesCbc192) { + blink::WebCryptoKey key; + Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192), true, + blink::WebCryptoKeyUsageEncrypt, &key); + ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status); +} + +// 192-bit AES is intentionally unsupported (http://crbug.com/533699). +TEST_F(WebCryptoAesCbcTest, UnwrapAesCbc192) { + std::vector<uint8_t> wrapping_key_data(16, 0); + std::vector<uint8_t> wrapped_key = HexStringToBytes( + "1A07ACAB6C906E50883173C29441DB1DE91D34F45C435B5F99C822867FB3956F"); + + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + wrapping_key_data, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), + blink::WebCryptoKeyUsageUnwrapKey); + + blink::WebCryptoKey unwrapped_key; + ASSERT_EQ( + Status::ErrorAes192BitUnsupported(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, CryptoData(wrapped_key), + wrapping_key, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); +} + +// Try importing an AES-CBC key with unsupported key usages using raw +// format. AES-CBC keys support the following usages: +// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' +TEST_F(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) { + const blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageDeriveBits, + blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, + }; + + std::vector<uint8_t> key_bytes(16); + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey key; + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(key_bytes), + algorithm, true, bad_usages[i], &key)); + } +} + +// Generate an AES-CBC key with invalid usages. AES-CBC supports: +// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' +TEST_F(WebCryptoAesCbcTest, GenerateKeyBadUsages) { + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageVerify, + }; + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey key; + + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, + bad_usages[i], &key)); + } +} + +// Generate an AES-CBC key with no usages. +TEST_F(WebCryptoAesCbcTest, GenerateKeyEmptyUsages) { + blink::WebCryptoKey key; + + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, 0, &key)); +} + +// Generate an AES-CBC key and an RSA key pair. Use the AES-CBC key to wrap the +// key pair (using SPKI format for public key, PKCS8 format for private key). +// Then unwrap the wrapped key pair and verify that the key data is the same. +TEST_F(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) { + // Generate the wrapping key. + blink::WebCryptoKey wrapping_key; + ASSERT_EQ(Status::Success(), + GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, + blink::WebCryptoKeyUsageWrapKey | + blink::WebCryptoKeyUsageUnwrapKey, + &wrapping_key)); + + // Generate an RSA key pair to be wrapped. + const unsigned int modulus_length = 256; + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + ASSERT_EQ(Status::Success(), + GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, + modulus_length, public_exponent), + true, blink::WebCryptoKeyUsageSign, &public_key, + &private_key)); + + // Export key pair as SPKI + PKCS8 + std::vector<uint8_t> public_key_spki; + ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatSpki, + public_key, &public_key_spki)); + + std::vector<uint8_t> private_key_pkcs8; + ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, + private_key, &private_key_pkcs8)); + + // Wrap the key pair. + blink::WebCryptoAlgorithm wrap_algorithm = + CreateAesCbcAlgorithm(std::vector<uint8_t>(16, 0)); + + std::vector<uint8_t> wrapped_public_key; + ASSERT_EQ(Status::Success(), + WrapKey(blink::WebCryptoKeyFormatSpki, public_key, wrapping_key, + wrap_algorithm, &wrapped_public_key)); + + std::vector<uint8_t> wrapped_private_key; + ASSERT_EQ(Status::Success(), + WrapKey(blink::WebCryptoKeyFormatPkcs8, private_key, wrapping_key, + wrap_algorithm, &wrapped_private_key)); + + // Unwrap the key pair. + blink::WebCryptoAlgorithm rsa_import_algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256); + + blink::WebCryptoKey unwrapped_public_key; + + ASSERT_EQ( + Status::Success(), + UnwrapKey(blink::WebCryptoKeyFormatSpki, CryptoData(wrapped_public_key), + wrapping_key, wrap_algorithm, rsa_import_algorithm, true, + blink::WebCryptoKeyUsageVerify, &unwrapped_public_key)); + + blink::WebCryptoKey unwrapped_private_key; + + ASSERT_EQ( + Status::Success(), + UnwrapKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(wrapped_private_key), + wrapping_key, wrap_algorithm, rsa_import_algorithm, true, + blink::WebCryptoKeyUsageSign, &unwrapped_private_key)); + + // Export unwrapped key pair as SPKI + PKCS8 + std::vector<uint8_t> unwrapped_public_key_spki; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatSpki, unwrapped_public_key, + &unwrapped_public_key_spki)); + + std::vector<uint8_t> unwrapped_private_key_pkcs8; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatPkcs8, unwrapped_private_key, + &unwrapped_private_key_pkcs8)); + + EXPECT_EQ(public_key_spki, unwrapped_public_key_spki); + EXPECT_EQ(private_key_pkcs8, unwrapped_private_key_pkcs8); + + EXPECT_NE(public_key_spki, wrapped_public_key); + EXPECT_NE(private_key_pkcs8, wrapped_private_key); +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/aes_ctr_openssl.cc b/chromium/components/webcrypto/algorithms/aes_ctr.cc index 26363f72b55..f4c16b63d75 100644 --- a/chromium/components/webcrypto/openssl/aes_ctr_openssl.cc +++ b/chromium/components/webcrypto/algorithms/aes_ctr.cc @@ -9,12 +9,11 @@ #include "base/macros.h" #include "base/numerics/safe_math.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/aes.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/aes_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -24,7 +23,7 @@ namespace webcrypto { namespace { const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) { - // BoringSSL does not support 192-bit AES keys. + // 192-bit AES is intentionally unsupported (http://crbug.com/533699). switch (key_length_bytes) { case 16: return EVP_aes_128_ctr(); @@ -149,8 +148,7 @@ Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& data, std::vector<uint8_t>* buffer) { const blink::WebCryptoAesCtrParams* params = algorithm.aesCtrParams(); - const std::vector<uint8_t>& raw_key = - SymKeyOpenSsl::Cast(key)->raw_key_data(); + const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key); if (params->counter().size() != 16) return Status::ErrorIncorrectSizeAesCtrCounter(); @@ -261,8 +259,8 @@ class AesCtrImplementation : public AesAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformAesCtrImplementation() { - return new AesCtrImplementation; +scoped_ptr<AlgorithmImplementation> CreateAesCtrImplementation() { + return make_scoped_ptr(new AesCtrImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc b/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc new file mode 100644 index 00000000000..9bdbf38dcdb --- /dev/null +++ b/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc @@ -0,0 +1,170 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Creates an AES-CTR algorithm for encryption/decryption. +blink::WebCryptoAlgorithm CreateAesCtrAlgorithm( + const std::vector<uint8_t>& counter, + uint8_t length_bits) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdAesCtr, + new blink::WebCryptoAesCtrParams( + length_bits, vector_as_array(&counter), + static_cast<unsigned int>(counter.size()))); +} + +class WebCryptoAesCtrTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoAesCtrTest, EncryptDecryptKnownAnswer) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_ctr.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + std::vector<uint8_t> test_counter = GetBytesFromHexString(test, "counter"); + int counter_length_bits = 0; + ASSERT_TRUE(test->GetInteger("length", &counter_length_bits)); + + std::vector<uint8_t> test_plain_text = + GetBytesFromHexString(test, "plain_text"); + std::vector<uint8_t> test_cipher_text = + GetBytesFromHexString(test, "cipher_text"); + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + test_key, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCtr), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + EXPECT_EQ(test_key.size() * 8, key.algorithm().aesParams()->lengthBits()); + + std::vector<uint8_t> output; + + // Test encryption. + EXPECT_EQ(Status::Success(), + Encrypt(CreateAesCtrAlgorithm(test_counter, counter_length_bits), + key, CryptoData(test_plain_text), &output)); + EXPECT_BYTES_EQ(test_cipher_text, output); + + // Test decryption. + EXPECT_EQ(Status::Success(), + Decrypt(CreateAesCtrAlgorithm(test_counter, counter_length_bits), + key, CryptoData(test_cipher_text), &output)); + EXPECT_BYTES_EQ(test_plain_text, output); + } +} + +// The counter block must be exactly 16 bytes. +TEST_F(WebCryptoAesCtrTest, InvalidCounterBlockLength) { + const unsigned int kBadCounterBlockLengthBytes[] = {0, 15, 17}; + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + std::vector<uint8>(16), // 128-bit key of all zeros. + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCtr), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + std::vector<uint8_t> input(32); + std::vector<uint8_t> output; + + for (size_t i = 0; i < arraysize(kBadCounterBlockLengthBytes); ++i) { + std::vector<uint8_t> bad_counter(kBadCounterBlockLengthBytes[i]); + + EXPECT_EQ(Status::ErrorIncorrectSizeAesCtrCounter(), + Encrypt(CreateAesCtrAlgorithm(bad_counter, 128), key, + CryptoData(input), &output)); + + EXPECT_EQ(Status::ErrorIncorrectSizeAesCtrCounter(), + Decrypt(CreateAesCtrAlgorithm(bad_counter, 128), key, + CryptoData(input), &output)); + } +} + +// The counter length cannot be less than 1 or greater than 128. +TEST_F(WebCryptoAesCtrTest, InvalidCounterLength) { + const uint8_t kBadCounterLengthBits[] = {0, 129}; + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + std::vector<uint8>(16), // 128-bit key of all zeros. + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCtr), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + std::vector<uint8_t> counter(16); + std::vector<uint8_t> input(32); + std::vector<uint8_t> output; + + for (size_t i = 0; i < arraysize(kBadCounterLengthBits); ++i) { + uint8_t bad_counter_length_bits = kBadCounterLengthBits[i]; + + EXPECT_EQ(Status::ErrorInvalidAesCtrCounterLength(), + Encrypt(CreateAesCtrAlgorithm(counter, bad_counter_length_bits), + key, CryptoData(input), &output)); + + EXPECT_EQ(Status::ErrorInvalidAesCtrCounterLength(), + Decrypt(CreateAesCtrAlgorithm(counter, bad_counter_length_bits), + key, CryptoData(input), &output)); + } +} + +// Tests wrap-around using a 4-bit counter. +// +// Wrap-around is allowed, however if the counter repeats itself an error should +// be thrown. +// +// Using a 4-bit counter it is possible to encrypt 16 blocks. However the 17th +// block would end up wrapping back to the starting value. +TEST_F(WebCryptoAesCtrTest, OverflowAndRepeatCounter) { + const uint8_t kCounterLengthBits = 4; + const uint8_t kStartCounter[] = {0, 1, 15}; + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + std::vector<uint8>(16), // 128-bit key of all zeros. + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCtr), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + std::vector<uint8_t> buffer(272); + + // 16 and 17 AES blocks worth of data respectively (AES blocks are 16 bytes + // long). + CryptoData input_16(vector_as_array(&buffer), 256); + CryptoData input_17(vector_as_array(&buffer), 272); + + std::vector<uint8_t> output; + + for (size_t i = 0; i < arraysize(kStartCounter); ++i) { + std::vector<uint8_t> counter(16); + counter[15] = kStartCounter[i]; + + // Baseline test: Encrypting 16 blocks should work (don't bother to check + // output, the known answer tests already do that). + EXPECT_EQ(Status::Success(), + Encrypt(CreateAesCtrAlgorithm(counter, kCounterLengthBits), key, + input_16, &output)); + + // Encrypting/Decrypting 17 however should fail. + EXPECT_EQ(Status::ErrorAesCtrInputTooLongCounterRepeated(), + Encrypt(CreateAesCtrAlgorithm(counter, kCounterLengthBits), key, + input_17, &output)); + EXPECT_EQ(Status::ErrorAesCtrInputTooLongCounterRepeated(), + Decrypt(CreateAesCtrAlgorithm(counter, kCounterLengthBits), key, + input_17, &output)); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/aes_gcm_openssl.cc b/chromium/components/webcrypto/algorithms/aes_gcm.cc index 564b54f88c7..3685bd93199 100644 --- a/chromium/components/webcrypto/openssl/aes_gcm_openssl.cc +++ b/chromium/components/webcrypto/algorithms/aes_gcm.cc @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <vector> #include <openssl/evp.h> +#include <vector> #include "base/logging.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/aes.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/aes_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -37,14 +36,21 @@ Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8_t>* buffer) { - const std::vector<uint8_t>& raw_key = - SymKeyOpenSsl::Cast(key)->raw_key_data(); + const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key); const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); - unsigned int tag_length_bits; - Status status = GetAesGcmTagLengthInBits(params, &tag_length_bits); - if (status.IsError()) - return status; + // The WebCrypto spec defines the default value for the tag length, as well as + // the allowed values for tag length. + unsigned int tag_length_bits = 128; + if (params->hasTagLengthBits()) { + tag_length_bits = params->optionalTagLengthBits(); + if (tag_length_bits != 32 && tag_length_bits != 64 && + tag_length_bits != 96 && tag_length_bits != 104 && + tag_length_bits != 112 && tag_length_bits != 120 && + tag_length_bits != 128) { + return Status::ErrorInvalidAesGcmTagLength(); + } + } return AeadEncryptDecrypt( mode, raw_key, data, tag_length_bits / 8, CryptoData(params->iv()), @@ -73,8 +79,8 @@ class AesGcmImplementation : public AesAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformAesGcmImplementation() { - return new AesGcmImplementation; +scoped_ptr<AlgorithmImplementation> CreateAesGcmImplementation() { + return make_scoped_ptr(new AesGcmImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc b/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc new file mode 100644 index 00000000000..94ba9bea803 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc @@ -0,0 +1,222 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Creates an AES-GCM algorithm. +blink::WebCryptoAlgorithm CreateAesGcmAlgorithm( + const std::vector<uint8_t>& iv, + const std::vector<uint8_t>& additional_data, + unsigned int tag_length_bits) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdAesGcm, + new blink::WebCryptoAesGcmParams( + vector_as_array(&iv), static_cast<unsigned int>(iv.size()), true, + vector_as_array(&additional_data), + static_cast<unsigned int>(additional_data.size()), true, + tag_length_bits)); +} + +blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm( + unsigned short key_length_bits) { + return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesGcm, + key_length_bits); +} + +Status AesGcmEncrypt(const blink::WebCryptoKey& key, + const std::vector<uint8_t>& iv, + const std::vector<uint8_t>& additional_data, + unsigned int tag_length_bits, + const std::vector<uint8_t>& plain_text, + std::vector<uint8_t>* cipher_text, + std::vector<uint8_t>* authentication_tag) { + blink::WebCryptoAlgorithm algorithm = + CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); + + std::vector<uint8_t> output; + Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output); + if (status.IsError()) + return status; + + if ((tag_length_bits % 8) != 0) { + ADD_FAILURE() << "Encrypt should have failed."; + return Status::OperationError(); + } + + size_t tag_length_bytes = tag_length_bits / 8; + + if (tag_length_bytes > output.size()) { + ADD_FAILURE() << "tag length is larger than output"; + return Status::OperationError(); + } + + // The encryption result is cipher text with authentication tag appended. + cipher_text->assign(output.begin(), + output.begin() + (output.size() - tag_length_bytes)); + authentication_tag->assign(output.begin() + cipher_text->size(), + output.end()); + + return Status::Success(); +} + +Status AesGcmDecrypt(const blink::WebCryptoKey& key, + const std::vector<uint8_t>& iv, + const std::vector<uint8_t>& additional_data, + unsigned int tag_length_bits, + const std::vector<uint8_t>& cipher_text, + const std::vector<uint8_t>& authentication_tag, + std::vector<uint8_t>* plain_text) { + blink::WebCryptoAlgorithm algorithm = + CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); + + // Join cipher text and authentication tag. + std::vector<uint8_t> cipher_text_with_tag; + cipher_text_with_tag.reserve(cipher_text.size() + authentication_tag.size()); + cipher_text_with_tag.insert(cipher_text_with_tag.end(), cipher_text.begin(), + cipher_text.end()); + cipher_text_with_tag.insert(cipher_text_with_tag.end(), + authentication_tag.begin(), + authentication_tag.end()); + + return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text); +} + +class WebCryptoAesGcmTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoAesGcmTest, GenerateKeyBadLength) { + const unsigned short kKeyLen[] = {0, 127, 257}; + blink::WebCryptoKey key; + for (size_t i = 0; i < arraysize(kKeyLen); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(Status::ErrorGenerateAesKeyLength(), + GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, + blink::WebCryptoKeyUsageDecrypt, &key)); + } +} + +TEST_F(WebCryptoAesGcmTest, GenerateKeyEmptyUsage) { + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(256), true, 0, &key)); +} + +TEST_F(WebCryptoAesGcmTest, ImportExportJwk) { + const blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm); + + // AES-GCM 128 + ImportExportJwkSymmetricKey( + 128, algorithm, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + "A128GCM"); + + // AES-GCM 256 + ImportExportJwkSymmetricKey(256, algorithm, blink::WebCryptoKeyUsageDecrypt, + "A256GCM"); +} + +// TODO(eroman): +// * Test decryption when the tag length exceeds input size +// * Test decryption with empty input +// * Test decryption with tag length of 0. +TEST_F(WebCryptoAesGcmTest, SampleSets) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests)); + + // Note that WebCrypto appends the authentication tag to the ciphertext. + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + const std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); + const std::vector<uint8_t> test_additional_data = + GetBytesFromHexString(test, "additional_data"); + const std::vector<uint8_t> test_plain_text = + GetBytesFromHexString(test, "plain_text"); + const std::vector<uint8_t> test_authentication_tag = + GetBytesFromHexString(test, "authentication_tag"); + const unsigned int test_tag_size_bits = + static_cast<unsigned int>(test_authentication_tag.size()) * 8; + const std::vector<uint8_t> test_cipher_text = + GetBytesFromHexString(test, "cipher_text"); + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + test_key, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + + // Verify exported raw key is identical to the imported data + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + + EXPECT_BYTES_EQ(test_key, raw_key); + + // Test encryption. + std::vector<uint8_t> cipher_text; + std::vector<uint8_t> authentication_tag; + EXPECT_EQ( + Status::Success(), + AesGcmEncrypt(key, test_iv, test_additional_data, test_tag_size_bits, + test_plain_text, &cipher_text, &authentication_tag)); + + EXPECT_BYTES_EQ(test_cipher_text, cipher_text); + EXPECT_BYTES_EQ(test_authentication_tag, authentication_tag); + + // Test decryption. + std::vector<uint8_t> plain_text; + EXPECT_EQ( + Status::Success(), + AesGcmDecrypt(key, test_iv, test_additional_data, test_tag_size_bits, + test_cipher_text, test_authentication_tag, &plain_text)); + EXPECT_BYTES_EQ(test_plain_text, plain_text); + + // Decryption should fail if any of the inputs are tampered with. + EXPECT_EQ(Status::OperationError(), + AesGcmDecrypt(key, Corrupted(test_iv), test_additional_data, + test_tag_size_bits, test_cipher_text, + test_authentication_tag, &plain_text)); + EXPECT_EQ(Status::OperationError(), + AesGcmDecrypt(key, test_iv, Corrupted(test_additional_data), + test_tag_size_bits, test_cipher_text, + test_authentication_tag, &plain_text)); + EXPECT_EQ(Status::OperationError(), + AesGcmDecrypt(key, test_iv, test_additional_data, + test_tag_size_bits, Corrupted(test_cipher_text), + test_authentication_tag, &plain_text)); + EXPECT_EQ(Status::OperationError(), + AesGcmDecrypt(key, test_iv, test_additional_data, + test_tag_size_bits, test_cipher_text, + Corrupted(test_authentication_tag), &plain_text)); + + // Try different incorrect tag lengths + uint8_t kAlternateTagLengths[] = {0, 8, 96, 120, 128, 160, 255}; + for (size_t tag_i = 0; tag_i < arraysize(kAlternateTagLengths); ++tag_i) { + unsigned int wrong_tag_size_bits = kAlternateTagLengths[tag_i]; + if (test_tag_size_bits == wrong_tag_size_bits) + continue; + EXPECT_NE(Status::Success(), + AesGcmDecrypt(key, test_iv, test_additional_data, + wrong_tag_size_bits, test_cipher_text, + test_authentication_tag, &plain_text)); + } + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/aes_kw_openssl.cc b/chromium/components/webcrypto/algorithms/aes_kw.cc index 31dacea3423..295d9114296 100644 --- a/chromium/components/webcrypto/openssl/aes_kw_openssl.cc +++ b/chromium/components/webcrypto/algorithms/aes_kw.cc @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <vector> #include <openssl/evp.h> +#include <vector> #include "base/logging.h" #include "base/numerics/safe_math.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/aes.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/aes_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" @@ -36,9 +36,8 @@ Status AesKwEncryptDecrypt(EncryptOrDecrypt mode, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8_t>* buffer) { - // These length checks are done so the returned error matches that of NSS - // implementation. Other than giving a more specific error, these are not - // required. + // These length checks are done in order to give a more specific error. These + // are not required for correctness. if ((mode == ENCRYPT && data.byte_length() < 16) || (mode == DECRYPT && data.byte_length() < 24)) { return Status::ErrorDataTooSmall(); @@ -46,8 +45,7 @@ Status AesKwEncryptDecrypt(EncryptOrDecrypt mode, if (data.byte_length() % 8) return Status::ErrorInvalidAesKwDataLength(); - const std::vector<uint8_t>& raw_key = - SymKeyOpenSsl::Cast(key)->raw_key_data(); + const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key); return AeadEncryptDecrypt(mode, raw_key, data, 8, // tag_length_bytes @@ -81,8 +79,8 @@ class AesKwImplementation : public AesAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformAesKwImplementation() { - return new AesKwImplementation; +scoped_ptr<AlgorithmImplementation> CreateAesKwImplementation() { + return make_scoped_ptr(new AesKwImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc new file mode 100644 index 00000000000..3002544a493 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc @@ -0,0 +1,538 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +blink::WebCryptoAlgorithm CreateAesKwKeyGenAlgorithm( + unsigned short key_length_bits) { + return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesKw, + key_length_bits); +} + +class WebCryptoAesKwTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoAesKwTest, GenerateKeyBadLength) { + const unsigned short kKeyLen[] = {0, 127, 257}; + blink::WebCryptoKey key; + for (size_t i = 0; i < arraysize(kKeyLen); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(Status::ErrorGenerateAesKeyLength(), + GenerateSecretKey(CreateAesKwKeyGenAlgorithm(kKeyLen[i]), true, + blink::WebCryptoKeyUsageWrapKey, &key)); + } +} + +TEST_F(WebCryptoAesKwTest, GenerateKeyEmptyUsage) { + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateSecretKey(CreateAesKwKeyGenAlgorithm(256), true, 0, &key)); +} + +TEST_F(WebCryptoAesKwTest, ImportKeyEmptyUsage) { + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(16)), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), true, + 0, &key)); +} + +TEST_F(WebCryptoAesKwTest, ImportKeyJwkKeyOpsWrapUnwrap) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + base::ListValue* key_ops = new base::ListValue; + dict.Set("key_ops", key_ops); // Takes ownership. + + key_ops->AppendString("wrapKey"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), false, + blink::WebCryptoKeyUsageWrapKey, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageWrapKey, key.usages()); + + key_ops->AppendString("unwrapKey"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), false, + blink::WebCryptoKeyUsageUnwrapKey, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageUnwrapKey, key.usages()); +} + +TEST_F(WebCryptoAesKwTest, ImportExportJwk) { + const blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // AES-KW 128 + ImportExportJwkSymmetricKey( + 128, algorithm, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, + "A128KW"); + + // AES-KW 256 + ImportExportJwkSymmetricKey( + 256, algorithm, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, + "A256KW"); +} + +TEST_F(WebCryptoAesKwTest, AesKwKeyImport) { + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // Import a 128-bit Key Encryption Key (KEK) + std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939"; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); + std::vector<uint8_t> key_raw_out; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); + EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); + + // Import a 192-bit KEK + key_raw_hex_in = "c0192c6466b2370decbb62b2cfef4384544ffeb4d2fbc103"; + ASSERT_EQ(Status::ErrorAes192BitUnsupported(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); + + // Import a 256-bit Key Encryption Key (KEK) + key_raw_hex_in = + "e11fe66380d90fa9ebefb74e0478e78f95664d0c67ca20ce4a0b5842863ac46f"; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); + EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); + + // Fail import of 0 length key + EXPECT_EQ( + Status::ErrorImportAesKeyLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(HexStringToBytes("")), + algorithm, true, blink::WebCryptoKeyUsageWrapKey, &key)); + + // Fail import of 124-bit KEK + key_raw_hex_in = "3e4566a2bdaa10cb68134fa66c15ddb"; + EXPECT_EQ(Status::ErrorImportAesKeyLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); + + // Fail import of 200-bit KEK + key_raw_hex_in = "0a1d88608a5ad9fec64f1ada269ebab4baa2feeb8d95638c0e"; + EXPECT_EQ(Status::ErrorImportAesKeyLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); + + // Fail import of 260-bit KEK + key_raw_hex_in = + "72d4e475ff34215416c9ad9c8281247a4d730c5f275ac23f376e73e3bce8d7d5a"; + EXPECT_EQ(Status::ErrorImportAesKeyLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm, + true, blink::WebCryptoKeyUsageWrapKey, &key)); +} + +TEST_F(WebCryptoAesKwTest, UnwrapFailures) { + // This test exercises the code path common to all unwrap operations. + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(0, &test)); + const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); + const std::vector<uint8_t> test_ciphertext = + GetBytesFromHexString(test, "ciphertext"); + + blink::WebCryptoKey unwrapped_key; + + // Using a wrapping algorithm that does not match the wrapping key algorithm + // should fail. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + test_kek, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), + blink::WebCryptoKeyUsageUnwrapKey); + EXPECT_EQ(Status::ErrorUnexpected(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, CryptoData(test_ciphertext), + wrapping_key, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); +} + +TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); + const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + const std::vector<uint8_t> test_ciphertext = + GetBytesFromHexString(test, "ciphertext"); + const blink::WebCryptoAlgorithm wrapping_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + test_kek, wrapping_algorithm, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); + + // Import the key to be wrapped. + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + test_key, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha1), + blink::WebCryptoKeyUsageSign); + + // Wrap the key and verify the ciphertext result against the known answer. + std::vector<uint8_t> wrapped_key; + ASSERT_EQ(Status::Success(), + WrapKey(blink::WebCryptoKeyFormatRaw, key, wrapping_key, + wrapping_algorithm, &wrapped_key)); + EXPECT_BYTES_EQ(test_ciphertext, wrapped_key); + + // Unwrap the known ciphertext to get a new test_key. + blink::WebCryptoKey unwrapped_key; + ASSERT_EQ( + Status::Success(), + UnwrapKey( + blink::WebCryptoKeyFormatRaw, CryptoData(test_ciphertext), + wrapping_key, wrapping_algorithm, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &unwrapped_key)); + EXPECT_FALSE(key.isNull()); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_EQ(true, key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); + + // Export the new key and compare its raw bytes with the original known key. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); + EXPECT_BYTES_EQ(test_key, raw_key); + } +} + +// Unwrap a HMAC key using AES-KW, and then try doing a sign/verify with the +// unwrapped key +TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); + + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(0, &test)); + const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); + const std::vector<uint8_t> test_ciphertext = + GetBytesFromHexString(test, "ciphertext"); + const blink::WebCryptoAlgorithm wrapping_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + test_kek, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); + + // Unwrap the known ciphertext. + blink::WebCryptoKey key; + ASSERT_EQ( + Status::Success(), + UnwrapKey( + blink::WebCryptoKeyFormatRaw, CryptoData(test_ciphertext), + wrapping_key, wrapping_algorithm, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha1), + false, blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + &key)); + + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_FALSE(key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + key.usages()); + + // Sign an empty message and ensure it is verified. + std::vector<uint8_t> test_message; + std::vector<uint8_t> signature; + + ASSERT_EQ(Status::Success(), + Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), key, + CryptoData(test_message), &signature)); + + EXPECT_GT(signature.size(), 0u); + + bool verify_result; + ASSERT_EQ( + Status::Success(), + Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), key, + CryptoData(signature), CryptoData(test_message), &verify_result)); +} + +TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); + base::DictionaryValue* test; + // Use 256 bits of data with a 256-bit KEK + ASSERT_TRUE(tests->GetDictionary(3, &test)); + const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); + const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + const std::vector<uint8_t> test_ciphertext = + GetBytesFromHexString(test, "ciphertext"); + const blink::WebCryptoAlgorithm wrapping_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + const blink::WebCryptoAlgorithm key_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); + // Import the wrapping key. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + test_kek, wrapping_algorithm, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); + // Import the key to be wrapped. + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + test_key, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + blink::WebCryptoKeyUsageEncrypt); + + // Unwrap with wrapped data too small must fail. + const std::vector<uint8_t> small_data(test_ciphertext.begin(), + test_ciphertext.begin() + 23); + blink::WebCryptoKey unwrapped_key; + EXPECT_EQ(Status::ErrorDataTooSmall(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, CryptoData(small_data), + wrapping_key, wrapping_algorithm, key_algorithm, true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); + + // Unwrap with wrapped data size not a multiple of 8 bytes must fail. + const std::vector<uint8_t> unaligned_data(test_ciphertext.begin(), + test_ciphertext.end() - 2); + EXPECT_EQ(Status::ErrorInvalidAesKwDataLength(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, CryptoData(unaligned_data), + wrapping_key, wrapping_algorithm, key_algorithm, true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); +} + +TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapCorruptData) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); + base::DictionaryValue* test; + // Use 256 bits of data with a 256-bit KEK + ASSERT_TRUE(tests->GetDictionary(3, &test)); + const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); + const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + const std::vector<uint8_t> test_ciphertext = + GetBytesFromHexString(test, "ciphertext"); + const blink::WebCryptoAlgorithm wrapping_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + test_kek, wrapping_algorithm, + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); + + // Unwrap of a corrupted version of the known ciphertext should fail, due to + // AES-KW's built-in integrity check. + blink::WebCryptoKey unwrapped_key; + EXPECT_EQ(Status::OperationError(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, + CryptoData(Corrupted(test_ciphertext)), wrapping_key, + wrapping_algorithm, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); +} + +TEST_F(WebCryptoAesKwTest, AesKwJwkSymkeyUnwrapKnownData) { + // The following data lists a known HMAC SHA-256 key, then a JWK + // representation of this key which was encrypted ("wrapped") using AES-KW and + // the following wrapping key. + // For reference, the intermediate clear JWK is + // {"alg":"HS256","ext":true,"k":<b64urlKey>,"key_ops":["verify"],"kty":"oct"} + // (Not shown is space padding to ensure the cleartext meets the size + // requirements of the AES-KW algorithm.) + const std::vector<uint8_t> key_data = HexStringToBytes( + "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"); + const std::vector<uint8_t> wrapped_key_data = HexStringToBytes( + "14E6380B35FDC5B72E1994764B6CB7BFDD64E7832894356AAEE6C3768FC3D0F115E6B0" + "6729756225F999AA99FDF81FD6A359F1576D3D23DE6CB69C3937054EB497AC1E8C38D5" + "5E01B9783A20C8D930020932CF25926103002213D0FC37279888154FEBCEDF31832158" + "97938C5CFE5B10B4254D0C399F39D0"); + const std::vector<uint8_t> wrapping_key_data = + HexStringToBytes("000102030405060708090A0B0C0D0E0F"); + const blink::WebCryptoAlgorithm wrapping_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( + wrapping_key_data, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); + + // Unwrap the known wrapped key data to produce a new key + blink::WebCryptoKey unwrapped_key; + ASSERT_EQ( + Status::Success(), + UnwrapKey( + blink::WebCryptoKeyFormatJwk, CryptoData(wrapped_key_data), + wrapping_key, wrapping_algorithm, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageVerify, &unwrapped_key)); + + // Validate the new key's attributes. + EXPECT_FALSE(unwrapped_key.isNull()); + EXPECT_TRUE(unwrapped_key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, unwrapped_key.type()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, unwrapped_key.algorithm().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + unwrapped_key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(256u, unwrapped_key.algorithm().hmacParams()->lengthBits()); + EXPECT_EQ(true, unwrapped_key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, unwrapped_key.usages()); + + // Export the new key's raw data and compare to the known original. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); + EXPECT_BYTES_EQ(key_data, raw_key); +} + +// Try importing an AES-KW key with unsupported key usages using raw +// format. AES-KW keys support the following usages: +// 'wrapKey', 'unwrapKey' +TEST_F(WebCryptoAesKwTest, ImportKeyBadUsage_Raw) { + const blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageEncrypt, + blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageUnwrapKey, + blink::WebCryptoKeyUsageDeriveBits, + blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, + }; + + std::vector<uint8_t> key_bytes(16); + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey key; + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(key_bytes), + algorithm, true, bad_usages[i], &key)); + } +} + +// Try unwrapping an HMAC key with unsupported usages using JWK format and +// AES-KW. HMAC keys support the following usages: +// 'sign', 'verify' +TEST_F(WebCryptoAesKwTest, UnwrapHmacKeyBadUsage_JWK) { + const blink::WebCryptoAlgorithm unwrap_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageEncrypt, + blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDeriveKey, + }; + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(16)), unwrap_algorithm, + true, blink::WebCryptoKeyUsageUnwrapKey, &wrapping_key)); + + // The JWK plain text is: + // {"kty":"oct","alg":"HS256","k":"GADWrMRHwQfoNaXU5fZvTg"} + const char* kWrappedJwk = + "C2B7F19A32EE31372CD40C9C969B8CD67553E5AEA7FD1144874584E46ABCD79FDC308848" + "B2DD8BD36A2D61062B9C5B8B499B8D6EF8EB320D87A614952B4EE771"; + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey key; + + ASSERT_EQ( + Status::ErrorCreateKeyBadUsages(), + UnwrapKey(blink::WebCryptoKeyFormatJwk, + CryptoData(HexStringToBytes(kWrappedJwk)), wrapping_key, + unwrap_algorithm, CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha256), + true, bad_usages[i], &key)); + } +} + +// Try unwrapping an RSA-SSA public key with unsupported usages using JWK format +// and AES-KW. RSA-SSA public keys support the following usages: +// 'verify' +TEST_F(WebCryptoAesKwTest, UnwrapRsaSsaPublicKeyBadUsage_JWK) { + const blink::WebCryptoAlgorithm unwrap_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageEncrypt, + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, + }; + + // Import the wrapping key. + blink::WebCryptoKey wrapping_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(16)), unwrap_algorithm, + true, blink::WebCryptoKeyUsageUnwrapKey, &wrapping_key)); + + // The JWK plaintext is: + // { "kty": "RSA","alg": "RS256","n": "...","e": "AQAB"} + + const char* kWrappedJwk = + "CE8DAEF99E977EE58958B8C4494755C846E883B2ECA575C5366622839AF71AB30875F152" + "E8E33E15A7817A3A2874EB53EFE05C774D98BC936BA9BA29BEB8BB3F3C3CE2323CB3359D" + "E3F426605CF95CCF0E01E870ABD7E35F62E030B5FB6E520A5885514D1D850FB64B57806D" + "1ADA57C6E27DF345D8292D80F6B074F1BE51C4CF3D76ECC8886218551308681B44FAC60B" + "8CF6EA439BC63239103D0AE81ADB96F908680586C6169284E32EB7DD09D31103EBDAC0C2" + "40C72DCF0AEA454113CC47457B13305B25507CBEAB9BDC8D8E0F867F9167F9DCEF0D9F9B" + "30F2EE83CEDFD51136852C8A5939B768"; + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey key; + + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + UnwrapKey(blink::WebCryptoKeyFormatJwk, + CryptoData(HexStringToBytes(kWrappedJwk)), wrapping_key, + unwrap_algorithm, + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, bad_usages[i], &key)); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/util_openssl.cc b/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc index b6ada7007e3..9abd47ce28c 100644 --- a/chromium/components/webcrypto/openssl/util_openssl.cc +++ b/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc @@ -1,20 +1,16 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/util_openssl.h" +#include "components/webcrypto/algorithms/asymmetric_key_util.h" -#include <openssl/evp.h> #include <openssl/pkcs12.h> -#include <openssl/rand.h> -#include "base/stl_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/platform_crypto.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" namespace webcrypto { @@ -61,110 +57,6 @@ Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) { } // namespace -void PlatformInit() { - crypto::EnsureOpenSSLInit(); -} - -const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { - switch (id) { - case blink::WebCryptoAlgorithmIdSha1: - return EVP_sha1(); - case blink::WebCryptoAlgorithmIdSha256: - return EVP_sha256(); - case blink::WebCryptoAlgorithmIdSha384: - return EVP_sha384(); - case blink::WebCryptoAlgorithmIdSha512: - return EVP_sha512(); - default: - return NULL; - } -} - -Status AeadEncryptDecrypt(EncryptOrDecrypt mode, - const std::vector<uint8_t>& raw_key, - const CryptoData& data, - unsigned int tag_length_bytes, - const CryptoData& iv, - const CryptoData& additional_data, - const EVP_AEAD* aead_alg, - std::vector<uint8_t>* buffer) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - EVP_AEAD_CTX ctx; - - if (!aead_alg) - return Status::ErrorUnexpected(); - - if (!EVP_AEAD_CTX_init(&ctx, aead_alg, vector_as_array(&raw_key), - raw_key.size(), tag_length_bytes, NULL)) { - return Status::OperationError(); - } - - crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup> ctx_cleanup(&ctx); - - size_t len; - int ok; - - if (mode == DECRYPT) { - if (data.byte_length() < tag_length_bytes) - return Status::ErrorDataTooSmall(); - - buffer->resize(data.byte_length() - tag_length_bytes); - - ok = EVP_AEAD_CTX_open(&ctx, vector_as_array(buffer), &len, buffer->size(), - iv.bytes(), iv.byte_length(), data.bytes(), - data.byte_length(), additional_data.bytes(), - additional_data.byte_length()); - } else { - // No need to check for unsigned integer overflow here (seal fails if - // the output buffer is too small). - buffer->resize(data.byte_length() + EVP_AEAD_max_overhead(aead_alg)); - - ok = EVP_AEAD_CTX_seal(&ctx, vector_as_array(buffer), &len, buffer->size(), - iv.bytes(), iv.byte_length(), data.bytes(), - data.byte_length(), additional_data.bytes(), - additional_data.byte_length()); - } - - if (!ok) - return Status::OperationError(); - buffer->resize(len); - return Status::Success(); -} - -Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - unsigned int keylen_bits, - GenerateKeyResult* result) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - unsigned int keylen_bytes = NumBitsToBytes(keylen_bits); - std::vector<unsigned char> random_bytes(keylen_bytes, 0); - - if (keylen_bytes > 0) { - if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) - return Status::OperationError(); - TruncateToBitLength(keylen_bits, &random_bytes); - } - - result->AssignSecretKey(blink::WebCryptoKey::create( - new SymKeyOpenSsl(CryptoData(random_bytes)), - blink::WebCryptoKeyTypeSecret, extractable, algorithm, usages)); - - return Status::Success(); -} - -Status CreateWebCryptoSecretKey(const CryptoData& key_data, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) { - *key = blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data), - blink::WebCryptoKeyTypeSecret, extractable, - algorithm, usages); - return Status::Success(); -} - Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, const blink::WebCryptoKeyAlgorithm& algorithm, bool extractable, @@ -178,7 +70,7 @@ Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, return status; *key = blink::WebCryptoKey::create( - new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)), + CreateAsymmetricKeyHandle(public_key.Pass(), spki_data), blink::WebCryptoKeyTypePublic, extractable, algorithm, usages); return Status::Success(); } @@ -196,11 +88,25 @@ Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, return status; *key = blink::WebCryptoKey::create( - new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)), + CreateAsymmetricKeyHandle(private_key.Pass(), pkcs8_data), blink::WebCryptoKeyTypePrivate, extractable, algorithm, usages); return Status::Success(); } +Status CheckPrivateKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages) { + return CheckKeyCreationUsages(all_possible_usages, actual_usages, + EmptyUsagePolicy::REJECT_EMPTY); +} + +Status CheckPublicKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages) { + return CheckKeyCreationUsages(all_possible_usages, actual_usages, + EmptyUsagePolicy::ALLOW_EMPTY); +} + Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, int expected_pkey_id, crypto::ScopedEVP_PKEY* pkey) { @@ -238,14 +144,51 @@ Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, return Status::Success(); } -BIGNUM* CreateBIGNUM(const std::string& n) { - return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); +Status VerifyUsagesBeforeImportAsymmetricKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask all_public_key_usages, + blink::WebCryptoKeyUsageMask all_private_key_usages, + blink::WebCryptoKeyUsageMask usages) { + switch (format) { + case blink::WebCryptoKeyFormatSpki: + return CheckPublicKeyCreationUsages(all_public_key_usages, usages); + case blink::WebCryptoKeyFormatPkcs8: + return CheckPrivateKeyCreationUsages(all_private_key_usages, usages); + case blink::WebCryptoKeyFormatJwk: { + // The JWK could represent either a public key or private key. The usages + // must make sense for one of the two. The usages will be checked again by + // ImportKeyJwk() once the key type has been determined. + if (CheckPublicKeyCreationUsages(all_public_key_usages, usages) + .IsError() && + CheckPrivateKeyCreationUsages(all_private_key_usages, usages) + .IsError()) { + return Status::ErrorCreateKeyBadUsages(); + } + return Status::Success(); + } + default: + return Status::ErrorUnsupportedImportKeyFormat(); + } } -std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n) { - std::vector<uint8_t> v(BN_num_bytes(n)); - BN_bn2bin(n, vector_as_array(&v)); - return v; +Status GetUsagesForGenerateAsymmetricKey( + blink::WebCryptoKeyUsageMask combined_usages, + blink::WebCryptoKeyUsageMask all_public_usages, + blink::WebCryptoKeyUsageMask all_private_usages, + blink::WebCryptoKeyUsageMask* public_usages, + blink::WebCryptoKeyUsageMask* private_usages) { + // Ensure that the combined usages is a subset of the total possible usages. + Status status = + CheckKeyCreationUsages(all_public_usages | all_private_usages, + combined_usages, EmptyUsagePolicy::ALLOW_EMPTY); + if (status.IsError()) + return status; + + *public_usages = combined_usages & all_public_usages; + *private_usages = combined_usages & all_private_usages; + + // Ensure that the private key has non-empty usages. + return CheckPrivateKeyCreationUsages(all_private_usages, *private_usages); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/asymmetric_key_util.h b/chromium/components/webcrypto/algorithms/asymmetric_key_util.h new file mode 100644 index 00000000000..51ed79a8d13 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/asymmetric_key_util.h @@ -0,0 +1,83 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_ASYMMETRIC_KEY_UTIL_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_ASYMMETRIC_KEY_UTIL_ + +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +// This file contains functions shared by multiple asymmetric key algorithms. + +namespace webcrypto { + +class CryptoData; +class Status; + +// Creates a WebCrypto public key given an EVP_PKEY. This step includes +// exporting the key to SPKI format, for use by serialization later. +Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Creates a WebCrypto private key given an EVP_PKEY. This step includes +// exporting the key to PKCS8 format, for use by serialization later. +Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Checks that a private key can be created using |actual_usages|, where +// |all_possible_usages| is the full set of allowed private key usages. Note +// that private keys are not allowed to have empty usages. +Status CheckPrivateKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages); + +// Checks that a public key can be created using |actual_usages|, where +// |all_possible_usages| is the full set of allowed public key usages +Status CheckPublicKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages); + +// Imports SPKI bytes to an EVP_PKEY for a public key. The resulting asymmetric +// key may be invalid, and should be verified using something like +// RSA_check_key(). The only validation performed by this function is to ensure +// the key type matched |expected_pkey_id|. +Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey); + +// Imports PKCS8 bytes to an EVP_PKEY for a private key. The resulting +// asymmetric key may be invalid, and should be verified using something like +// RSA_check_key(). The only validation performed by this function is to ensure +// the key type matched |expected_pkey_id|. +Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, + int expected_pkey_id, + crypto::ScopedEVP_PKEY* pkey); + +// Verifies that |usages| is valid when importing a key of the given format. +Status VerifyUsagesBeforeImportAsymmetricKey( + blink::WebCryptoKeyFormat format, + blink::WebCryptoKeyUsageMask all_public_key_usages, + blink::WebCryptoKeyUsageMask all_private_key_usages, + blink::WebCryptoKeyUsageMask usages); + +// Splits the combined usages given to GenerateKey() into the respective usages +// for the public key and private key. Returns an error if the usages are +// invalid. +Status GetUsagesForGenerateAsymmetricKey( + blink::WebCryptoKeyUsageMask combined_usages, + blink::WebCryptoKeyUsageMask all_public_usages, + blink::WebCryptoKeyUsageMask all_private_usages, + blink::WebCryptoKeyUsageMask* public_usages, + blink::WebCryptoKeyUsageMask* private_usages); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_ASYMMETRIC_KEY_UTIL_ diff --git a/chromium/components/webcrypto/openssl/ec_algorithm_openssl.cc b/chromium/components/webcrypto/algorithms/ec.cc index 85f95eabe3d..20baa57f6d2 100644 --- a/chromium/components/webcrypto/openssl/ec_algorithm_openssl.cc +++ b/chromium/components/webcrypto/algorithms/ec.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/ec_algorithm_openssl.h" +#include "components/webcrypto/algorithms/ec.h" #include <openssl/ec.h> #include <openssl/ec_key.h> @@ -11,13 +11,13 @@ #include "base/logging.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/asymmetric_key_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" #include "components/webcrypto/jwk.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -209,6 +209,15 @@ Status GetPublicKey(EC_KEY* ec, return Status::Success(); } +// Synthesizes an import algorithm given a key algorithm, so that +// deserialization can re-use the ImportKey*() methods. +blink::WebCryptoAlgorithm SynthesizeImportAlgorithmForClone( + const blink::WebCryptoKeyAlgorithm& algorithm) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + algorithm.id(), new blink::WebCryptoEcKeyImportParams( + algorithm.ecParams()->namedCurve())); +} + } // namespace Status EcAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm, @@ -379,9 +388,12 @@ Status EcAlgorithm::ImportKeyJwk(const CryptoData& key_data, bool is_private_key = jwk.HasMember("d"); // Now that the key type is known, verify the usages. - status = CheckKeyCreationUsages( - is_private_key ? all_private_key_usages_ : all_public_key_usages_, usages, - !is_private_key); + if (is_private_key) { + status = CheckPrivateKeyCreationUsages(all_private_key_usages_, usages); + } else { + status = CheckPublicKeyCreationUsages(all_public_key_usages_, usages); + } + if (status.IsError()) return status; @@ -448,7 +460,10 @@ Status EcAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { if (key.type() != blink::WebCryptoKeyTypePrivate) return Status::ErrorUnexpectedKeyType(); - *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + // This relies on the fact that PKCS8 formatted data was already + // associated with the key during its creation (used by + // structured clone). + *buffer = GetSerializedKeyData(key); return Status::Success(); } @@ -456,7 +471,10 @@ Status EcAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { if (key.type() != blink::WebCryptoKeyTypePublic) return Status::ErrorUnexpectedKeyType(); - *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + // This relies on the fact that SPKI formatted data was already + // associated with the key during its creation (used by + // structured clone). + *buffer = GetSerializedKeyData(key); return Status::Success(); } @@ -466,7 +484,7 @@ Status EcAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); + EVP_PKEY* pkey = GetEVP_PKEY(key); crypto::ScopedEC_KEY ec(EVP_PKEY_get1_EC_KEY(pkey)); if (!ec.get()) @@ -511,13 +529,6 @@ Status EcAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, return Status::Success(); } -Status EcAlgorithm::SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const { - key_data->assign(AsymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); -} - // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763 Status EcAlgorithm::DeserializeKeyForClone( const blink::WebCryptoKeyAlgorithm& algorithm, @@ -526,11 +537,12 @@ Status EcAlgorithm::DeserializeKeyForClone( blink::WebCryptoKeyUsageMask usages, const CryptoData& key_data, blink::WebCryptoKey* key) const { - blink::WebCryptoAlgorithm import_algorithm = CreateEcImportAlgorithm( - algorithm.id(), algorithm.ecParams()->namedCurve()); + blink::WebCryptoAlgorithm import_algorithm = + SynthesizeImportAlgorithmForClone(algorithm); Status status; + // The serialized data will be either SPKI or PKCS8 formatted. switch (type) { case blink::WebCryptoKeyTypePublic: status = diff --git a/chromium/components/webcrypto/openssl/ec_algorithm_openssl.h b/chromium/components/webcrypto/algorithms/ec.h index 9771f3d6142..bebf87bf089 100644 --- a/chromium/components/webcrypto/openssl/ec_algorithm_openssl.h +++ b/chromium/components/webcrypto/algorithms/ec.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_EC_ALGORITHM_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_EC_ALGORITHM_OPENSSL_H_ +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_EC_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_EC_H_ #include "components/webcrypto/algorithm_implementation.h" @@ -61,10 +61,6 @@ class EcAlgorithm : public AlgorithmImplementation { Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const override; - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override; - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -79,4 +75,4 @@ class EcAlgorithm : public AlgorithmImplementation { } // namespace webcrypto -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_EC_ALGORITHM_OPENSSL_H_ +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_EC_H_ diff --git a/chromium/components/webcrypto/openssl/ecdh_openssl.cc b/chromium/components/webcrypto/algorithms/ecdh.cc index c0b264d2fc2..9da36e97411 100644 --- a/chromium/components/webcrypto/openssl/ecdh_openssl.cc +++ b/chromium/components/webcrypto/algorithms/ecdh.cc @@ -9,13 +9,12 @@ #include "base/logging.h" #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/ec.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/openssl/ec_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "crypto/secure_util.h" @@ -79,13 +78,13 @@ class EcdhImplementation : public EcAlgorithm { } crypto::ScopedEC_KEY public_key_ec( - EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key())); + EVP_PKEY_get1_EC_KEY(GetEVP_PKEY(public_key))); const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key_ec.get()); crypto::ScopedEC_KEY private_key_ec( - EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(base_key)->key())); + EVP_PKEY_get1_EC_KEY(GetEVP_PKEY(base_key))); // The size of the shared secret is the field size in bytes (rounded up). // Note that, if rounding was required, the most significant bits of the @@ -126,8 +125,8 @@ class EcdhImplementation : public EcAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformEcdhImplementation() { - return new EcdhImplementation; +scoped_ptr<AlgorithmImplementation> CreateEcdhImplementation() { + return make_scoped_ptr(new EcdhImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/ecdh_unittest.cc b/chromium/components/webcrypto/algorithms/ecdh_unittest.cc new file mode 100644 index 00000000000..961c6d5fa0c --- /dev/null +++ b/chromium/components/webcrypto/algorithms/ecdh_unittest.cc @@ -0,0 +1,331 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/ec.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// TODO(eroman): Test passing an RSA public key instead of ECDH key. +// TODO(eroman): Test passing an ECDSA public key + +blink::WebCryptoAlgorithm CreateEcdhImportAlgorithm( + blink::WebCryptoNamedCurve named_curve) { + return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdh, named_curve); +} + +blink::WebCryptoAlgorithm CreateEcdhDeriveParams( + const blink::WebCryptoKey& public_key) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdEcdh, + new blink::WebCryptoEcdhKeyDeriveParams(public_key)); +} + +blink::WebCryptoAlgorithm CreateAesGcmDerivedKeyParams( + unsigned short length_bits) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdAesGcm, + new blink::WebCryptoAesDerivedKeyParams(length_bits)); +} + +// Helper that loads a "public_key" and "private_key" from the test data. +bool ImportKeysFromTest(const base::DictionaryValue* test, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) { + // Import the public key. + const base::DictionaryValue* public_key_json = NULL; + EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json)); + blink::WebCryptoNamedCurve curve = + GetCurveNameFromDictionary(public_key_json); + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, + CryptoData(MakeJsonVector(*public_key_json)), + CreateEcdhImportAlgorithm(curve), true, 0, public_key)); + + // If the test didn't specify an error for private key import, that implies + // it expects success. + std::string expected_private_key_error = "Success"; + test->GetString("private_key_error", &expected_private_key_error); + + // Import the private key. + const base::DictionaryValue* private_key_json = NULL; + EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json)); + curve = GetCurveNameFromDictionary(private_key_json); + Status status = ImportKey( + blink::WebCryptoKeyFormatJwk, + CryptoData(MakeJsonVector(*private_key_json)), + CreateEcdhImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageDeriveBits | blink::WebCryptoKeyUsageDeriveKey, + private_key); + EXPECT_EQ(expected_private_key_error, StatusToString(status)); + return status.IsSuccess(); +} + +class WebCryptoEcdhTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + // Import the keys. + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + if (!ImportKeysFromTest(test, &public_key, &private_key)) + continue; + + // Now try to derive bytes. + std::vector<uint8_t> derived_bytes; + int length_bits = 0; + ASSERT_TRUE(test->GetInteger("length_bits", &length_bits)); + + // If the test didn't specify an error, that implies it expects success. + std::string expected_error = "Success"; + test->GetString("error", &expected_error); + + Status status = DeriveBits(CreateEcdhDeriveParams(public_key), private_key, + length_bits, &derived_bytes); + ASSERT_EQ(expected_error, StatusToString(status)); + if (status.IsError()) + continue; + + std::vector<uint8_t> expected_bytes = + GetBytesFromHexString(test, "derived_bytes"); + + EXPECT_EQ(CryptoData(expected_bytes), CryptoData(derived_bytes)); + } +} + +// Loads up a test ECDH public and private key for P-521. The keys +// come from different key pairs, and can be used for key derivation of up to +// 528 bits. +::testing::AssertionResult LoadTestKeys(blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) { + scoped_ptr<base::ListValue> tests; + if (!ReadJsonTestFileToList("ecdh.json", &tests)) + return ::testing::AssertionFailure() << "Failed loading ecdh.json"; + + const base::DictionaryValue* test = NULL; + bool valid_p521_keys = false; + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + EXPECT_TRUE(tests->GetDictionary(test_index, &test)); + test->GetBoolean("valid_p521_keys", &valid_p521_keys); + if (valid_p521_keys) + break; + } + if (!valid_p521_keys) { + return ::testing::AssertionFailure() + << "The P-521 test are missing in ecdh.json"; + } + + ImportKeysFromTest(test, public_key, private_key); + + EXPECT_EQ(blink::WebCryptoNamedCurveP521, + public_key->algorithm().ecParams()->namedCurve()); + + return ::testing::AssertionSuccess(); +} + +// Try deriving an AES key of length 129 bits. +TEST_F(WebCryptoEcdhTest, DeriveKeyBadAesLength) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + ASSERT_EQ(Status::ErrorGetAesKeyLength(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), + CreateAesGcmDerivedKeyParams(129), true, + blink::WebCryptoKeyUsageEncrypt, &derived_key)); +} + +// Try deriving an AES key of length 192 bits. +TEST_F(WebCryptoEcdhTest, DeriveKeyUnsupportedAesLength) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + ASSERT_EQ(Status::ErrorAes192BitUnsupported(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), + CreateAesGcmDerivedKeyParams(192), true, + blink::WebCryptoKeyUsageEncrypt, &derived_key)); +} + +// Try deriving an HMAC key of length 0 bits. +TEST_F(WebCryptoEcdhTest, DeriveKeyZeroLengthHmac) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); + + ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + import_algorithm, import_algorithm, true, + blink::WebCryptoKeyUsageSign, &derived_key)); +} + +// Derive an HMAC key of length 19 bits. +TEST_F(WebCryptoEcdhTest, DeriveKeyHmac19Bits) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 19); + + ASSERT_EQ(Status::Success(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + import_algorithm, import_algorithm, true, + blink::WebCryptoKeyUsageSign, &derived_key)); + + ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id()); + ASSERT_EQ(blink::WebCryptoAlgorithmIdSha1, + derived_key.algorithm().hmacParams()->hash().id()); + ASSERT_EQ(19u, derived_key.algorithm().hmacParams()->lengthBits()); + + // Export the key and verify its contents. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); + EXPECT_EQ(3u, raw_key.size()); + // The last 7 bits of the key should be zero. + EXPECT_EQ(0, raw_key[raw_key.size() - 1] & 0x1f); +} + +// Derive an HMAC key with no specified length (just the hash of SHA-256). +TEST_F(WebCryptoEcdhTest, DeriveKeyHmacSha256NoLength) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256); + + ASSERT_EQ(Status::Success(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + import_algorithm, import_algorithm, true, + blink::WebCryptoKeyUsageSign, &derived_key)); + + ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id()); + ASSERT_EQ(blink::WebCryptoAlgorithmIdSha256, + derived_key.algorithm().hmacParams()->hash().id()); + ASSERT_EQ(512u, derived_key.algorithm().hmacParams()->lengthBits()); + + // Export the key and verify its contents. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); + EXPECT_EQ(64u, raw_key.size()); +} + +// Derive an HMAC key with no specified length (just the hash of SHA-512). +// +// This fails, because ECDH using P-521 can only generate 528 bits, however HMAC +// SHA-512 requires 1024 bits. +// +// In practice, authors won't be directly generating keys from key agreement +// schemes, as that is frequently insecure, and instead be using KDFs to expand +// and generate keys. For simplicity of testing, however, test using an HMAC +// key. +TEST_F(WebCryptoEcdhTest, DeriveKeyHmacSha512NoLength) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512); + + ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + import_algorithm, import_algorithm, true, + blink::WebCryptoKeyUsageSign, &derived_key)); +} + +// Try deriving an AES key of length 128 bits. +TEST_F(WebCryptoEcdhTest, DeriveKeyAes128) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey base_key; + ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); + + blink::WebCryptoKey derived_key; + + ASSERT_EQ(Status::Success(), + DeriveKey(CreateEcdhDeriveParams(public_key), base_key, + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), + CreateAesGcmDerivedKeyParams(128), true, + blink::WebCryptoKeyUsageEncrypt, &derived_key)); + + ASSERT_EQ(blink::WebCryptoAlgorithmIdAesGcm, derived_key.algorithm().id()); + ASSERT_EQ(128, derived_key.algorithm().aesParams()->lengthBits()); + + // Export the key and verify its contents. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); + EXPECT_EQ(16u, raw_key.size()); +} + +TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) { + blink::WebCryptoKey key; + + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests)); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(0, &test)); + + // Import the public key. + const base::DictionaryValue* public_key_json = NULL; + EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json)); + blink::WebCryptoNamedCurve curve = + GetCurveNameFromDictionary(public_key_json); + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, + CryptoData(MakeJsonVector(*public_key_json)), + CreateEcdhImportAlgorithm(curve), true, 0, &key)); + EXPECT_EQ(0, key.usages()); + + // Import the private key. + const base::DictionaryValue* private_key_json = NULL; + EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json)); + curve = GetCurveNameFromDictionary(private_key_json); + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatJwk, + CryptoData(MakeJsonVector(*private_key_json)), + CreateEcdhImportAlgorithm(curve), true, 0, &key)); +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/ecdsa_openssl.cc b/chromium/components/webcrypto/algorithms/ecdsa.cc index 53bbcbeb159..367e0f0ef4a 100644 --- a/chromium/components/webcrypto/openssl/ecdsa_openssl.cc +++ b/chromium/components/webcrypto/algorithms/ecdsa.cc @@ -9,13 +9,12 @@ #include "base/logging.h" #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/ec.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/openssl/ec_algorithm_openssl.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "crypto/secure_util.h" @@ -33,8 +32,8 @@ Status GetPKeyAndDigest(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, EVP_PKEY** pkey, const EVP_MD** digest) { - *pkey = AsymKeyOpenSsl::Cast(key)->key(); - *digest = GetDigest(algorithm.ecdsaParams()->hash().id()); + *pkey = GetEVP_PKEY(key); + *digest = GetDigest(algorithm.ecdsaParams()->hash()); if (!*digest) return Status::ErrorUnsupported(); return Status::Success(); @@ -259,8 +258,8 @@ class EcdsaImplementation : public EcAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformEcdsaImplementation() { - return new EcdsaImplementation; +scoped_ptr<AlgorithmImplementation> CreateEcdsaImplementation() { + return make_scoped_ptr(new EcdsaImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc new file mode 100644 index 00000000000..4b2e6515ce6 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc @@ -0,0 +1,349 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +blink::WebCryptoAlgorithm CreateEcdsaKeyGenAlgorithm( + blink::WebCryptoNamedCurve named_curve) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdEcdsa, + new blink::WebCryptoEcKeyGenParams(named_curve)); +} + +blink::WebCryptoAlgorithm CreateEcdsaImportAlgorithm( + blink::WebCryptoNamedCurve named_curve) { + return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdsa, named_curve); +} + +blink::WebCryptoAlgorithm CreateEcdsaAlgorithm( + blink::WebCryptoAlgorithmId hash_id) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdEcdsa, + new blink::WebCryptoEcdsaParams(CreateAlgorithm(hash_id))); +} + +class WebCryptoEcdsaTest : public WebCryptoTestBase {}; + +// Generates some ECDSA key pairs. Validates basic properties on the keys, and +// ensures the serialized key (as JWK) is unique. This test does nothing to +// ensure that the keys are otherwise usable (by trying to sign/verify with +// them). +TEST_F(WebCryptoEcdsaTest, GenerateKeyIsRandom) { + blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256; + + std::vector<std::vector<uint8_t>> serialized_keys; + + // Generate a small sample of keys. + for (int j = 0; j < 4; ++j) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_EQ(Status::Success(), + GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true, + blink::WebCryptoKeyUsageSign, &public_key, + &private_key)); + + // Basic sanity checks on the generated key pair. + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); + EXPECT_EQ(named_curve, public_key.algorithm().ecParams()->namedCurve()); + EXPECT_EQ(named_curve, private_key.algorithm().ecParams()->namedCurve()); + + // Export the key pair to JWK. + std::vector<uint8_t> key_bytes; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &key_bytes)); + serialized_keys.push_back(key_bytes); + + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, private_key, &key_bytes)); + serialized_keys.push_back(key_bytes); + } + + // Ensure all entries in the key sample set are unique. This is a simplistic + // estimate of whether the generated keys appear random. + EXPECT_FALSE(CopiesExist(serialized_keys)); +} + +TEST_F(WebCryptoEcdsaTest, GenerateKeyEmptyUsage) { + blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256; + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true, 0, + &public_key, &private_key)); +} + +// Verify that ECDSA signatures are probabilistic. Signing the same message two +// times should yield different signatures. However both signatures should +// verify correctly. +TEST_F(WebCryptoEcdsaTest, SignatureIsRandom) { + // Import a public and private keypair from "ec_private_keys.json". It doesn't + // really matter which one is used since they are all valid. In this case + // using the first one. + scoped_ptr<base::ListValue> private_keys; + ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &private_keys)); + const base::DictionaryValue* key_dict; + ASSERT_TRUE(private_keys->GetDictionary(0, &key_dict)); + blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(key_dict); + const base::DictionaryValue* key_jwk; + ASSERT_TRUE(key_dict->GetDictionary("jwk", &key_jwk)); + + blink::WebCryptoKey private_key; + ASSERT_EQ( + Status::Success(), + ImportKeyJwkFromDict(*key_jwk, CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageSign, &private_key)); + + // Erase the "d" member so the private key JWK can be used to import the + // public key (WebCrypto doesn't provide a mechanism for importing a public + // key given a private key). + scoped_ptr<base::DictionaryValue> key_jwk_copy(key_jwk->DeepCopy()); + key_jwk_copy->Remove("d", NULL); + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict(*key_jwk_copy.get(), + CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageVerify, &public_key)); + + // Sign twice + std::vector<uint8_t> message(10); + blink::WebCryptoAlgorithm algorithm = + CreateEcdsaAlgorithm(blink::WebCryptoAlgorithmIdSha1); + + std::vector<uint8_t> signature1; + std::vector<uint8_t> signature2; + ASSERT_EQ(Status::Success(), + Sign(algorithm, private_key, CryptoData(message), &signature1)); + ASSERT_EQ(Status::Success(), + Sign(algorithm, private_key, CryptoData(message), &signature2)); + + // The two signatures should be different. + EXPECT_NE(CryptoData(signature1), CryptoData(signature2)); + + // And both should be valid signatures which can be verified. + bool signature_matches; + ASSERT_EQ(Status::Success(), + Verify(algorithm, public_key, CryptoData(signature1), + CryptoData(message), &signature_matches)); + EXPECT_TRUE(signature_matches); + ASSERT_EQ(Status::Success(), + Verify(algorithm, public_key, CryptoData(signature2), + CryptoData(message), &signature_matches)); + EXPECT_TRUE(signature_matches); +} + +// Tests verify() for ECDSA using an assortment of keys, curves and hashes. +// These tests also include expected failures for bad signatures and keys. +TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("ecdsa.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test); + blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); + std::vector<uint8_t> key_data = + GetKeyDataFromJsonTestCase(test, key_format); + + // If the test didn't specify an error, that implies it expects success. + std::string expected_error = "Success"; + test->GetString("error", &expected_error); + + // Import the public key. + blink::WebCryptoKey key; + Status status = ImportKey(key_format, CryptoData(key_data), + CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageVerify, &key); + ASSERT_EQ(expected_error, StatusToString(status)); + if (status.IsError()) + continue; + + // Basic sanity checks on the imported public key. + EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); + EXPECT_EQ(curve, key.algorithm().ecParams()->namedCurve()); + + // Now try to verify the given message and signature. + std::vector<uint8_t> message = GetBytesFromHexString(test, "msg"); + std::vector<uint8_t> signature = GetBytesFromHexString(test, "sig"); + blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash"); + + bool verify_result; + status = Verify(CreateEcdsaAlgorithm(hash.id()), key, CryptoData(signature), + CryptoData(message), &verify_result); + ASSERT_EQ(expected_error, StatusToString(status)); + if (status.IsError()) + continue; + + // If no error was expected, the verification's boolean must match + // "verify_result" for the test. + bool expected_result = false; + ASSERT_TRUE(test->GetBoolean("verify_result", &expected_result)); + EXPECT_EQ(expected_result, verify_result); + } +} + +// The test file may include either public or private keys. In order to import +// them successfully, the correct usages need to be specified. This function +// determines what usages to use for the key. +blink::WebCryptoKeyUsageMask GetExpectedUsagesForKeyImport( + blink::WebCryptoKeyFormat key_format, + const base::DictionaryValue* test) { + blink::WebCryptoKeyUsageMask kPublicUsages = blink::WebCryptoKeyUsageVerify; + blink::WebCryptoKeyUsageMask kPrivateUsages = blink::WebCryptoKeyUsageSign; + + switch (key_format) { + case blink::WebCryptoKeyFormatRaw: + case blink::WebCryptoKeyFormatSpki: + return kPublicUsages; + case blink::WebCryptoKeyFormatPkcs8: + return kPrivateUsages; + break; + case blink::WebCryptoKeyFormatJwk: { + const base::DictionaryValue* key = NULL; + if (!test->GetDictionary("key", &key)) + ADD_FAILURE() << "Missing key property"; + return key->HasKey("d") ? kPrivateUsages : kPublicUsages; + } + } + + // Appease compiler. + return kPrivateUsages; +} + +// Tests importing bad public/private keys in a variety of formats. +TEST_F(WebCryptoEcdsaTest, ImportBadKeys) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("bad_ec_keys.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test); + blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); + std::vector<uint8_t> key_data = + GetKeyDataFromJsonTestCase(test, key_format); + std::string expected_error; + ASSERT_TRUE(test->GetString("error", &expected_error)); + + blink::WebCryptoKey key; + Status status = ImportKey( + key_format, CryptoData(key_data), CreateEcdsaImportAlgorithm(curve), + true, GetExpectedUsagesForKeyImport(key_format, test), &key); + ASSERT_EQ(expected_error, StatusToString(status)); + } +} + +// Tests importing and exporting of EC private keys, using both JWK and PKCS8 +// formats. +// +// The test imports a key first using JWK, and then exporting it to JWK and +// PKCS8. It does the same thing using PKCS8 as the original source of truth. +TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test); + const base::DictionaryValue* jwk_dict; + EXPECT_TRUE(test->GetDictionary("jwk", &jwk_dict)); + std::vector<uint8_t> jwk_bytes = MakeJsonVector(*jwk_dict); + std::vector<uint8_t> pkcs8_bytes = GetBytesFromHexString( + test, test->HasKey("exported_pkcs8") ? "exported_pkcs8" : "pkcs8"); + + // ------------------------------------------------- + // Test from JWK, and then export to {JWK, PKCS8} + // ------------------------------------------------- + + // Import the key using JWK + blink::WebCryptoKey key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes), + CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageSign, &key)); + + // Export the key as JWK + std::vector<uint8_t> exported_bytes; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes)); + + // NOTE: The exported bytes can't be directly compared to jwk_bytes because + // the exported JWK differs from the imported one. In particular it contains + // extra properties for extractability and key_ops. + // + // Verification is instead done by using the first exported JWK bytes as the + // expectation. + jwk_bytes = exported_bytes; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes), + CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageSign, &key)); + + // Export the key as JWK (again) + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes)); + EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes)); + + // Export the key as PKCS8 + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes)); + EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes)); + + // ------------------------------------------------- + // Test from PKCS8, and then export to {JWK, PKCS8} + // ------------------------------------------------- + + // The imported PKCS8 bytes may differ from the exported bytes (in the case + // where the publicKey was missing, it will be synthesized and written back + // during export). + std::vector<uint8_t> pkcs8_input_bytes = GetBytesFromHexString( + test, test->HasKey("original_pkcs8") ? "original_pkcs8" : "pkcs8"); + CryptoData pkcs8_input_data(pkcs8_input_bytes.empty() ? pkcs8_bytes + : pkcs8_input_bytes); + + // Import the key using PKCS8 + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, pkcs8_input_data, + CreateEcdsaImportAlgorithm(curve), true, + blink::WebCryptoKeyUsageSign, &key)); + + // Export the key as PKCS8 + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes)); + EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes)); + + // Export the key as JWK + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes)); + EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes)); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/hkdf_openssl.cc b/chromium/components/webcrypto/algorithms/hkdf.cc index 9a1e2084bcb..0ccb857919c 100644 --- a/chromium/components/webcrypto/openssl/hkdf_openssl.cc +++ b/chromium/components/webcrypto/algorithms/hkdf.cc @@ -8,11 +8,11 @@ #include "base/logging.h" #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/secret_key_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" @@ -33,7 +33,7 @@ class HkdfImplementation : public AlgorithmImplementation { blink::WebCryptoKeyUsageMask usages) const override { if (format != blink::WebCryptoKeyFormatRaw) return Status::ErrorUnsupportedImportKeyFormat(); - return CheckKeyCreationUsages(kValidUsages, usages, false); + return CheckSecretKeyCreationUsages(kValidUsages, usages); } Status ImportKeyRaw(const CryptoData& key_data, @@ -58,7 +58,7 @@ class HkdfImplementation : public AlgorithmImplementation { const blink::WebCryptoHkdfParams* params = algorithm.hkdfParams(); - const EVP_MD* digest_algorithm = GetDigest(params->hash().id()); + const EVP_MD* digest_algorithm = GetDigest(params->hash()); if (!digest_algorithm) return Status::ErrorUnsupported(); @@ -68,8 +68,7 @@ class HkdfImplementation : public AlgorithmImplementation { // Algorithm dispatch checks that the algorithm in |base_key| matches // |algorithm|. - const std::vector<uint8_t>& raw_key = - SymKeyOpenSsl::Cast(base_key)->raw_key_data(); + const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(base_key); if (!HKDF(vector_as_array(derived_bytes), derived_bytes_len, digest_algorithm, vector_as_array(&raw_key), raw_key.size(), params->salt().data(), params->salt().size(), @@ -86,13 +85,6 @@ class HkdfImplementation : public AlgorithmImplementation { return Status::Success(); } - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override { - key_data->assign(SymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); - } - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -113,8 +105,8 @@ class HkdfImplementation : public AlgorithmImplementation { } // namespace -AlgorithmImplementation* CreatePlatformHkdfImplementation() { - return new HkdfImplementation; +scoped_ptr<AlgorithmImplementation> CreateHkdfImplementation() { + return make_scoped_ptr(new HkdfImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/hmac_openssl.cc b/chromium/components/webcrypto/algorithms/hmac.cc index 9962bfd8c55..28ef3a05ac2 100644 --- a/chromium/components/webcrypto/openssl/hmac_openssl.cc +++ b/chromium/components/webcrypto/algorithms/hmac.cc @@ -5,14 +5,15 @@ #include <openssl/hmac.h> #include "base/logging.h" +#include "base/numerics/safe_math.h" #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/secret_key_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/jwk.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/secure_util.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -22,6 +23,62 @@ namespace webcrypto { namespace { +Status GetDigestBlockSizeBits(const blink::WebCryptoAlgorithm& algorithm, + unsigned int* block_size_bits) { + const EVP_MD* md = GetDigest(algorithm); + if (!md) + return Status::ErrorUnsupported(); + *block_size_bits = static_cast<unsigned int>(8 * EVP_MD_block_size(md)); + return Status::Success(); +} + +// Gets the requested key length in bits for an HMAC import operation. +Status GetHmacImportKeyLengthBits( + const blink::WebCryptoHmacImportParams* params, + unsigned int key_data_byte_length, + unsigned int* keylen_bits) { + if (key_data_byte_length == 0) + return Status::ErrorHmacImportEmptyKey(); + + // Make sure that the key data's length can be represented in bits without + // overflow. + base::CheckedNumeric<unsigned int> checked_keylen_bits(key_data_byte_length); + checked_keylen_bits *= 8; + + if (!checked_keylen_bits.IsValid()) + return Status::ErrorDataTooLarge(); + + unsigned int data_keylen_bits = checked_keylen_bits.ValueOrDie(); + + // Determine how many bits of the input to use. + *keylen_bits = data_keylen_bits; + if (params->hasLengthBits()) { + // The requested bit length must be: + // * No longer than the input data length + // * At most 7 bits shorter. + if (NumBitsToBytes(params->optionalLengthBits()) != key_data_byte_length) + return Status::ErrorHmacImportBadLength(); + *keylen_bits = params->optionalLengthBits(); + } + + return Status::Success(); +} + +const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { + switch (hash) { + case blink::WebCryptoAlgorithmIdSha1: + return "HS1"; + case blink::WebCryptoAlgorithmIdSha256: + return "HS256"; + case blink::WebCryptoAlgorithmIdSha384: + return "HS384"; + case blink::WebCryptoAlgorithmIdSha512: + return "HS512"; + default: + return NULL; + } +} + const blink::WebCryptoKeyUsageMask kAllKeyUsages = blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; @@ -31,7 +88,7 @@ Status SignHmac(const std::vector<uint8_t>& raw_key, std::vector<uint8_t>* buffer) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - const EVP_MD* digest_algorithm = GetDigest(hash.id()); + const EVP_MD* digest_algorithm = GetDigest(hash); if (!digest_algorithm) return Status::ErrorUnsupported(); size_t hmac_expected_length = EVP_MD_size(digest_algorithm); @@ -58,7 +115,7 @@ class HmacImplementation : public AlgorithmImplementation { bool extractable, blink::WebCryptoKeyUsageMask usages, GenerateKeyResult* result) const override { - Status status = CheckKeyCreationUsages(kAllKeyUsages, usages, false); + Status status = CheckSecretKeyCreationUsages(kAllKeyUsages, usages); if (status.IsError()) return status; @@ -66,9 +123,16 @@ class HmacImplementation : public AlgorithmImplementation { algorithm.hmacKeyGenParams(); unsigned int keylen_bits = 0; - status = GetHmacKeyGenLengthInBits(params, &keylen_bits); - if (status.IsError()) - return status; + if (params->hasLengthBits()) { + keylen_bits = params->optionalLengthBits(); + // Zero-length HMAC keys are disallowed by the spec. + if (keylen_bits == 0) + return Status::ErrorGenerateHmacKeyLengthZero(); + } else { + status = GetDigestBlockSizeBits(params->hash(), &keylen_bits); + if (status.IsError()) + return status; + } return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::createHmac( params->hash().id(), keylen_bits), @@ -81,7 +145,7 @@ class HmacImplementation : public AlgorithmImplementation { switch (format) { case blink::WebCryptoKeyFormatRaw: case blink::WebCryptoKeyFormatJwk: - return CheckKeyCreationUsages(kAllKeyUsages, usages, false); + return CheckSecretKeyCreationUsages(kAllKeyUsages, usages); default: return Status::ErrorUnsupportedImportKeyFormat(); } @@ -130,8 +194,12 @@ class HmacImplementation : public AlgorithmImplementation { return Status::ErrorUnexpected(); std::vector<uint8_t> raw_data; - Status status = ReadSecretKeyJwk(key_data, algorithm_name, extractable, - usages, &raw_data); + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages, + &raw_data, &jwk); + if (status.IsError()) + return status; + status = jwk.VerifyAlg(algorithm_name); if (status.IsError()) return status; @@ -141,14 +209,13 @@ class HmacImplementation : public AlgorithmImplementation { Status ExportKeyRaw(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const override { - *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); + *buffer = GetSymmetricKeyData(key); return Status::Success(); } Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const override { - SymKeyOpenSsl* sym_key = SymKeyOpenSsl::Cast(key); - const std::vector<uint8_t>& raw_data = sym_key->raw_key_data(); + const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key); const char* algorithm_name = GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); @@ -168,8 +235,7 @@ class HmacImplementation : public AlgorithmImplementation { const blink::WebCryptoAlgorithm& hash = key.algorithm().hmacParams()->hash(); - return SignHmac(SymKeyOpenSsl::Cast(key)->raw_key_data(), hash, data, - buffer); + return SignHmac(GetSymmetricKeyData(key), hash, data, buffer); } Status Verify(const blink::WebCryptoAlgorithm& algorithm, @@ -192,13 +258,6 @@ class HmacImplementation : public AlgorithmImplementation { return Status::Success(); } - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override { - key_data->assign(SymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); - } - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -212,14 +271,25 @@ class HmacImplementation : public AlgorithmImplementation { Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, bool* has_length_bits, unsigned int* length_bits) const override { - return GetHmacKeyLength(key_length_algorithm, has_length_bits, length_bits); + const blink::WebCryptoHmacImportParams* params = + key_length_algorithm.hmacImportParams(); + + *has_length_bits = true; + if (params->hasLengthBits()) { + *length_bits = params->optionalLengthBits(); + if (*length_bits == 0) + return Status::ErrorGetHmacKeyLengthZero(); + return Status::Success(); + } + + return GetDigestBlockSizeBits(params->hash(), length_bits); } }; } // namespace -AlgorithmImplementation* CreatePlatformHmacImplementation() { - return new HmacImplementation; +scoped_ptr<AlgorithmImplementation> CreateHmacImplementation() { + return make_scoped_ptr(new HmacImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/hmac_unittest.cc b/chromium/components/webcrypto/algorithms/hmac_unittest.cc new file mode 100644 index 00000000000..4c8a67aa513 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/hmac_unittest.cc @@ -0,0 +1,602 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Creates an HMAC algorithm whose parameters struct is compatible with key +// generation. It is an error to call this with a hash_id that is not a SHA*. +// The key_length_bits parameter is optional, with zero meaning unspecified. +blink::WebCryptoAlgorithm CreateHmacKeyGenAlgorithm( + blink::WebCryptoAlgorithmId hash_id, + unsigned int key_length_bits) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + // key_length_bytes == 0 means unspecified + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdHmac, + new blink::WebCryptoHmacKeyGenParams( + CreateAlgorithm(hash_id), (key_length_bits != 0), key_length_bits)); +} + +blink::WebCryptoAlgorithm CreateHmacImportAlgorithmWithLength( + blink::WebCryptoAlgorithmId hash_id, + unsigned int length_bits) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdHmac, + new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id), true, + length_bits)); +} + +class WebCryptoHmacTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoHmacTest, HMACSampleSets) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("hmac.json", &tests)); + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoAlgorithm test_hash = GetDigestAlgorithm(test, "hash"); + const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); + const std::vector<uint8_t> test_message = + GetBytesFromHexString(test, "message"); + const std::vector<uint8_t> test_mac = GetBytesFromHexString(test, "mac"); + + blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac); + + blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithmNoLength(test_hash.id()); + + blink::WebCryptoKey key = ImportSecretKeyFromRaw( + test_key, import_algorithm, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify); + + EXPECT_EQ(test_hash.id(), key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(test_key.size() * 8, key.algorithm().hmacParams()->lengthBits()); + + // Verify exported raw key is identical to the imported data + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_BYTES_EQ(test_key, raw_key); + + std::vector<uint8_t> output; + + ASSERT_EQ(Status::Success(), + Sign(algorithm, key, CryptoData(test_message), &output)); + + EXPECT_BYTES_EQ(test_mac, output); + + bool signature_match = false; + EXPECT_EQ(Status::Success(), + Verify(algorithm, key, CryptoData(output), + CryptoData(test_message), &signature_match)); + EXPECT_TRUE(signature_match); + + // Ensure truncated signature does not verify by passing one less byte. + EXPECT_EQ(Status::Success(), + Verify(algorithm, key, + CryptoData(vector_as_array(&output), + static_cast<unsigned int>(output.size()) - 1), + CryptoData(test_message), &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure truncated signature does not verify by passing no bytes. + EXPECT_EQ(Status::Success(), + Verify(algorithm, key, CryptoData(), CryptoData(test_message), + &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure extra long signature does not cause issues and fails. + const unsigned char kLongSignature[1024] = {0}; + EXPECT_EQ(Status::Success(), + Verify(algorithm, key, + CryptoData(kLongSignature, sizeof(kLongSignature)), + CryptoData(test_message), &signature_match)); + EXPECT_FALSE(signature_match); + } +} + +TEST_F(WebCryptoHmacTest, GenerateKeyIsRandom) { + // Generate a small sample of HMAC keys. + std::vector<std::vector<uint8_t>> keys; + for (int i = 0; i < 16; ++i) { + std::vector<uint8_t> key_bytes; + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 512); + ASSERT_EQ( + Status::Success(), + GenerateSecretKey(algorithm, true, blink::WebCryptoKeyUsageSign, &key)); + EXPECT_FALSE(key.isNull()); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); + + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_EQ(64U, raw_key.size()); + keys.push_back(raw_key); + } + // Ensure all entries in the key sample set are unique. This is a simplistic + // estimate of whether the generated keys appear random. + EXPECT_FALSE(CopiesExist(keys)); +} + +// If the key length is not provided, then the block size is used. +TEST_F(WebCryptoHmacTest, GenerateKeyNoLengthSha1) { + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); + ASSERT_EQ( + Status::Success(), + GenerateSecretKey(algorithm, true, blink::WebCryptoKeyUsageSign, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_EQ(64U, raw_key.size()); +} + +// If the key length is not provided, then the block size is used. +TEST_F(WebCryptoHmacTest, GenerateKeyNoLengthSha512) { + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha512, 0); + ASSERT_EQ( + Status::Success(), + GenerateSecretKey(algorithm, true, blink::WebCryptoKeyUsageSign, &key)); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha512, + key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(1024u, key.algorithm().hmacParams()->lengthBits()); + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_EQ(128U, raw_key.size()); +} + +TEST_F(WebCryptoHmacTest, GenerateKeyEmptyUsage) { + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha512, 0); + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateSecretKey(algorithm, true, 0, &key)); +} + +// Generate a 1 bit key. The exported key is 1 byte long, and 7 of the bits are +// guaranteed to be zero. +TEST_F(WebCryptoHmacTest, Generate1BitKey) { + blink::WebCryptoKey key; + blink::WebCryptoAlgorithm algorithm = + CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 1); + + ASSERT_EQ( + Status::Success(), + GenerateSecretKey(algorithm, true, blink::WebCryptoKeyUsageSign, &key)); + EXPECT_EQ(1u, key.algorithm().hmacParams()->lengthBits()); + + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + ASSERT_EQ(1U, raw_key.size()); + + EXPECT_FALSE(raw_key[0] & 0x7F); +} + +TEST_F(WebCryptoHmacTest, ImportKeyEmptyUsage) { + blink::WebCryptoKey key; + std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939"; + EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(HexStringToBytes(key_raw_hex_in)), + CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha1), + true, 0, &key)); +} + +TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + base::ListValue* key_ops = new base::ListValue; + dict.Set("key_ops", key_ops); // Takes ownership. + + key_ops->AppendString("sign"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha256), + false, blink::WebCryptoKeyUsageSign, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); + + key_ops->AppendString("verify"); + + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha256), + false, blink::WebCryptoKeyUsageVerify, &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); +} + +// Test 'use' inconsistent with 'key_ops'. +TEST_F(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + base::ListValue* key_ops = new base::ListValue; + dict.Set("key_ops", key_ops); // Takes ownership. + + dict.SetString("alg", "HS256"); + dict.SetString("use", "sig"); + key_ops->AppendString("sign"); + key_ops->AppendString("verify"); + key_ops->AppendString("encrypt"); + EXPECT_EQ( + Status::ErrorJwkUseAndKeyopsInconsistent(), + ImportKeyJwkFromDict( + dict, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256), + false, blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + &key)); +} + +// Test JWK composite 'sig' use +TEST_F(WebCryptoHmacTest, ImportKeyJwkUseSig) { + blink::WebCryptoKey key; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg"); + + dict.SetString("use", "sig"); + EXPECT_EQ( + Status::Success(), + ImportKeyJwkFromDict( + dict, + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256), + false, blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + &key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + key.usages()); +} + +TEST_F(WebCryptoHmacTest, ImportJwkInputConsistency) { + // The Web Crypto spec says that if a JWK value is present, but is + // inconsistent with the input value, the operation must fail. + + // Consistency rules when JWK value is not present: Inputs should be used. + blink::WebCryptoKey key; + bool extractable = false; + blink::WebCryptoAlgorithm algorithm = + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256); + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageVerify; + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); + std::vector<uint8_t> json_vec = MakeJsonVector(dict); + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + algorithm, extractable, usages, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(extractable, key.extractable()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + key.algorithm().hmacParams()->hash().id()); + EXPECT_EQ(320u, key.algorithm().hmacParams()->lengthBits()); + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); + key = blink::WebCryptoKey::createNull(); + + // Consistency rules when JWK value exists: Fail if inconsistency is found. + + // Pass: All input values are consistent with the JWK values. + dict.Clear(); + dict.SetString("kty", "oct"); + dict.SetString("alg", "HS256"); + dict.SetString("use", "sig"); + dict.SetBoolean("ext", false); + dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); + json_vec = MakeJsonVector(dict); + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + algorithm, extractable, usages, &key)); + + // Extractable cases: + // 1. input=T, JWK=F ==> fail (inconsistent) + // 4. input=F, JWK=F ==> pass, result extractable is F + // 2. input=T, JWK=T ==> pass, result extractable is T + // 3. input=F, JWK=T ==> pass, result extractable is F + EXPECT_EQ(Status::ErrorJwkExtInconsistent(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + algorithm, true, usages, &key)); + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + algorithm, false, usages, &key)); + EXPECT_FALSE(key.extractable()); + dict.SetBoolean("ext", true); + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, true, usages, &key)); + EXPECT_TRUE(key.extractable()); + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); + EXPECT_FALSE(key.extractable()); + dict.SetBoolean("ext", true); // restore previous value + + // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value + // (HMAC SHA256). + dict.Clear(); + dict.SetString("kty", "oct"); + dict.SetString("alg", "HS256"); + dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); + EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(), + ImportKeyJwkFromDict( + dict, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + extractable, blink::WebCryptoKeyUsageEncrypt, &key)); + // Fail: Input usage (encrypt) is inconsistent with JWK value (use=sig). + EXPECT_EQ(Status::ErrorJwkUseInconsistent(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), + extractable, blink::WebCryptoKeyUsageEncrypt, &key)); + + // Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value + // (HMAC SHA256). + EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha1), + extractable, usages, &key)); + + // Pass: JWK alg missing but input algorithm specified: use input value + dict.Remove("alg", NULL); + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha256), + extractable, usages, &key)); + EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, algorithm.id()); + dict.SetString("alg", "HS256"); + + // Fail: Input usages (encrypt) is not a subset of the JWK value + // (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. + EXPECT_EQ( + Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), algorithm, + extractable, blink::WebCryptoKeyUsageEncrypt, &key)); + + // Fail: Input usages (encrypt|sign|verify) is not a subset of the JWK + // value (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. + usages = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign | + blink::WebCryptoKeyUsageVerify; + EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json_vec), + algorithm, extractable, usages, &key)); + + // TODO(padolph): kty vs alg consistency tests: Depending on the kty value, + // only certain alg values are permitted. For example, when kty = "RSA" alg + // must be of the RSA family, or when kty = "oct" alg must be symmetric + // algorithm. + + // TODO(padolph): key_ops consistency tests +} + +TEST_F(WebCryptoHmacTest, ImportJwkHappy) { + // This test verifies the happy path of JWK import, including the application + // of the imported key material. + + blink::WebCryptoKey key; + bool extractable = false; + blink::WebCryptoAlgorithm algorithm = + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256); + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + + // Import a symmetric key JWK and HMAC-SHA256 sign() + // Uses the first SHA256 test vector from the HMAC sample set above. + + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("alg", "HS256"); + dict.SetString("use", "sig"); + dict.SetBoolean("ext", false); + dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); + + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, extractable, usages, &key)); + + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + key.algorithm().hmacParams()->hash().id()); + + const std::vector<uint8_t> message_raw = HexStringToBytes( + "b1689c2591eaf3c9e66070f8a77954ffb81749f1b00346f9dfe0b2ee905dcc288baf4a" + "92de3f4001dd9f44c468c3d07d6c6ee82faceafc97c2fc0fc0601719d2dcd0aa2aec92" + "d1b0ae933c65eb06a03c9c935c2bad0459810241347ab87e9f11adb30415424c6c7f5f" + "22a003b8ab8de54f6ded0e3ab9245fa79568451dfa258e"); + + std::vector<uint8_t> output; + + ASSERT_EQ(Status::Success(), + Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), key, + CryptoData(message_raw), &output)); + + const std::string mac_raw = + "769f00d3e6a6cc1fb426a14a4f76c6462e6149726e0dee0ec0cf97a16605ac8b"; + + EXPECT_BYTES_EQ_HEX(mac_raw, output); + + // TODO(padolph): Import an RSA public key JWK and use it +} + +TEST_F(WebCryptoHmacTest, ImportExportJwk) { + // HMAC SHA-1 + ImportExportJwkSymmetricKey( + 256, CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha1), + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, "HS1"); + + // HMAC SHA-384 + ImportExportJwkSymmetricKey( + 384, CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha384), + blink::WebCryptoKeyUsageSign, "HS384"); + + // HMAC SHA-512 + ImportExportJwkSymmetricKey( + 512, CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512), + blink::WebCryptoKeyUsageVerify, "HS512"); +} + +TEST_F(WebCryptoHmacTest, ExportJwkEmptyKey) { + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + + // Importing empty HMAC key is no longer allowed. However such a key can be + // created via de-serialization. + blink::WebCryptoKey key; + ASSERT_TRUE(DeserializeKeyForClone(blink::WebCryptoKeyAlgorithm::createHmac( + blink::WebCryptoAlgorithmIdSha1, 0), + blink::WebCryptoKeyTypeSecret, true, + usages, CryptoData(), &key)); + + // Export the key in JWK format and validate. + std::vector<uint8_t> json; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); + EXPECT_TRUE(VerifySecretJwk(json, "HS1", "", usages)); + + // Now try re-importing the JWK key. + key = blink::WebCryptoKey::createNull(); + EXPECT_EQ(Status::ErrorHmacImportEmptyKey(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json), + CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha1), + true, usages, &key)); +} + +// Imports an HMAC key contaning no byte data. +TEST_F(WebCryptoHmacTest, ImportRawEmptyKey) { + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha1); + + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + blink::WebCryptoKey key; + + ASSERT_EQ(Status::ErrorHmacImportEmptyKey(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(), + import_algorithm, true, usages, &key)); +} + +// Imports an HMAC key contaning 1 byte data, however the length was set to 0. +TEST_F(WebCryptoHmacTest, ImportRawKeyWithZeroLength) { + const blink::WebCryptoAlgorithm import_algorithm = + CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); + + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + blink::WebCryptoKey key; + + std::vector<uint8_t> key_data(1); + ASSERT_EQ(Status::ErrorHmacImportBadLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(key_data), + import_algorithm, true, usages, &key)); +} + +// Import a huge hmac key (UINT_MAX bytes). This will fail before actually +// reading the bytes, as the key is too large. +TEST_F(WebCryptoHmacTest, ImportRawKeyTooLarge) { + CryptoData big_data(NULL, UINT_MAX); // Invalid data of big length. + + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorDataTooLarge(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(big_data), + CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &key)); +} + +// Import an HMAC key with 120 bits of data, however request 128 bits worth. +TEST_F(WebCryptoHmacTest, ImportRawKeyLengthTooLarge) { + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorHmacImportBadLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(15)), + CreateHmacImportAlgorithmWithLength( + blink::WebCryptoAlgorithmIdSha1, 128), + true, blink::WebCryptoKeyUsageSign, &key)); +} + +// Import an HMAC key with 128 bits of data, however request 120 bits worth. +TEST_F(WebCryptoHmacTest, ImportRawKeyLengthTooSmall) { + blink::WebCryptoKey key; + EXPECT_EQ(Status::ErrorHmacImportBadLength(), + ImportKey(blink::WebCryptoKeyFormatRaw, + CryptoData(std::vector<uint8_t>(16)), + CreateHmacImportAlgorithmWithLength( + blink::WebCryptoAlgorithmIdSha1, 120), + true, blink::WebCryptoKeyUsageSign, &key)); +} + +// Import an HMAC key with 16 bits of data and request a 12 bit key, using the +// "raw" format. +TEST_F(WebCryptoHmacTest, ImportRawKeyTruncation) { + const std::vector<uint8_t> data = HexStringToBytes("b1ff"); + + blink::WebCryptoKey key; + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(data), + CreateHmacImportAlgorithmWithLength( + blink::WebCryptoAlgorithmIdSha1, 12), + true, blink::WebCryptoKeyUsageSign, &key)); + + // On export the last 4 bits has been set to zero. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_BYTES_EQ(HexStringToBytes("b1f0"), raw_key); +} + +// The same test as above, but using the JWK format. +TEST_F(WebCryptoHmacTest, ImportJwkKeyTruncation) { + base::DictionaryValue dict; + dict.SetString("kty", "oct"); + dict.SetString("k", "sf8"); // 0xB1FF + + blink::WebCryptoKey key; + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, CreateHmacImportAlgorithmWithLength( + blink::WebCryptoAlgorithmIdSha1, 12), + true, blink::WebCryptoKeyUsageSign, &key)); + + // On export the last 4 bits has been set to zero. + std::vector<uint8_t> raw_key; + EXPECT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + EXPECT_BYTES_EQ(HexStringToBytes("b1f0"), raw_key); +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/pbkdf2_openssl.cc b/chromium/components/webcrypto/algorithms/pbkdf2.cc index c4ce8466ba6..b5cf5ca0229 100644 --- a/chromium/components/webcrypto/openssl/pbkdf2_openssl.cc +++ b/chromium/components/webcrypto/algorithms/pbkdf2.cc @@ -4,11 +4,11 @@ #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/secret_key_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" @@ -29,7 +29,7 @@ class Pbkdf2Implementation : public AlgorithmImplementation { blink::WebCryptoKeyUsageMask usages) const override { switch (format) { case blink::WebCryptoKeyFormatRaw: - return CheckKeyCreationUsages(kAllKeyUsages, usages, false); + return CheckSecretKeyCreationUsages(kAllKeyUsages, usages); default: return Status::ErrorUnsupportedImportKeyFormat(); } @@ -63,16 +63,14 @@ class Pbkdf2Implementation : public AlgorithmImplementation { const blink::WebCryptoPbkdf2Params* params = algorithm.pbkdf2Params(); - const blink::WebCryptoAlgorithm& hash = params->hash(); - const EVP_MD* digest_algorithm = GetDigest(hash.id()); + const EVP_MD* digest_algorithm = GetDigest(params->hash()); if (!digest_algorithm) return Status::ErrorUnsupported(); unsigned int keylen_bytes = optional_length_bits / 8; derived_bytes->resize(keylen_bytes); - const std::vector<uint8_t>& password = - SymKeyOpenSsl::Cast(base_key)->raw_key_data(); + const std::vector<uint8_t>& password = GetSymmetricKeyData(base_key); if (!PKCS5_PBKDF2_HMAC( reinterpret_cast<const char*>(vector_as_array(&password)), @@ -84,13 +82,6 @@ class Pbkdf2Implementation : public AlgorithmImplementation { return Status::Success(); } - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override { - key_data->assign(SymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); - } - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -111,8 +102,8 @@ class Pbkdf2Implementation : public AlgorithmImplementation { } // namespace -AlgorithmImplementation* CreatePlatformPbkdf2Implementation() { - return new Pbkdf2Implementation; +scoped_ptr<AlgorithmImplementation> CreatePbkdf2Implementation() { + return make_scoped_ptr(new Pbkdf2Implementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc b/chromium/components/webcrypto/algorithms/rsa.cc index 9857a071fc6..892a5a0ec13 100644 --- a/chromium/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc +++ b/chromium/components/webcrypto/algorithms/rsa.cc @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" +#include "components/webcrypto/algorithms/rsa.h" #include <openssl/evp.h> #include "base/logging.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/asymmetric_key_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" #include "components/webcrypto/jwk.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -24,6 +24,106 @@ namespace webcrypto { namespace { +// Describes the RSA components for a parsed key. The names of the properties +// correspond with those from the JWK spec. Note that Chromium's WebCrypto +// implementation does not support multi-primes, so there is no parsed field +// for "oth". +struct JwkRsaInfo { + bool is_private_key = false; + std::string n; + std::string e; + std::string d; + std::string p; + std::string q; + std::string dp; + std::string dq; + std::string qi; +}; + +// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to +// |*result|. Returns Status::Success() on success, otherwise an error. +// In order for this to succeed: +// * expected_alg must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usages must be a subset of the JWK's "key_ops" if present. +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + JwkRsaInfo* result) { + JwkReader jwk; + Status status = jwk.Init(key_data, expected_extractable, expected_usages, + "RSA", expected_alg); + if (status.IsError()) + return status; + + // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry + // in the JWK, while an RSA private key must have those, plus at least a "d" + // (private exponent) entry. + // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, + // section 6.3. + status = jwk.GetBigInteger("n", &result->n); + if (status.IsError()) + return status; + status = jwk.GetBigInteger("e", &result->e); + if (status.IsError()) + return status; + + result->is_private_key = jwk.HasMember("d"); + if (!result->is_private_key) + return Status::Success(); + + status = jwk.GetBigInteger("d", &result->d); + if (status.IsError()) + return status; + + // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA + // spec. However they are required by Chromium's WebCrypto implementation. + + status = jwk.GetBigInteger("p", &result->p); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("q", &result->q); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("dp", &result->dp); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("dq", &result->dq); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("qi", &result->qi); + if (status.IsError()) + return status; + + return Status::Success(); +} + +// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, +// to unsigned int. +bool BigIntegerToUint(const uint8_t* data, + size_t data_size, + unsigned int* result) { + if (data_size == 0) + return false; + + *result = 0; + for (size_t i = 0; i < data_size; ++i) { + size_t reverse_i = data_size - i - 1; + + if (reverse_i >= sizeof(*result) && data[i]) + return false; // Too large for a uint. + + *result |= data[i] << 8 * reverse_i; + } + return true; +} + // Creates a blink::WebCryptoAlgorithm having the modulus length and public // exponent of |key|. Status CreateRsaHashedKeyAlgorithm( @@ -110,8 +210,7 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, return Status::OperationError(); } - // TODO(eroman): This should really be a DataError, however for compatibility - // with NSS it is an OperationError. + // TODO(eroman): This should be a DataError. if (!RSA_check_key(rsa.get())) return Status::OperationError(); @@ -149,6 +248,22 @@ Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, extractable, usages, key); } +// Converts a BIGNUM to a big endian byte array. +std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n) { + std::vector<uint8_t> v(BN_num_bytes(n)); + BN_bn2bin(n, vector_as_array(&v)); + return v; +} + +// Synthesizes an import algorithm given a key algorithm, so that +// deserialization can re-use the ImportKey*() methods. +blink::WebCryptoAlgorithm SynthesizeImportAlgorithmForClone( + const blink::WebCryptoKeyAlgorithm& algorithm) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + algorithm.id(), new blink::WebCryptoRsaHashedImportParams( + algorithm.rsaHashedParams()->hash())); +} + } // namespace Status RsaHashedAlgorithm::GenerateKey( @@ -168,12 +283,29 @@ Status RsaHashedAlgorithm::GenerateKey( const blink::WebCryptoRsaHashedKeyGenParams* params = algorithm.rsaHashedKeyGenParams(); + unsigned int modulus_length_bits = params->modulusLengthBits(); + + // Limit the RSA key sizes to: + // * Multiple of 8 bits + // * 256 bits to 16K bits + // + // These correspond with limitations at the time there was an NSS WebCrypto + // implementation. However in practice the upper bound is also helpful + // because generating large RSA keys is very slow. + if (modulus_length_bits < 256 || modulus_length_bits > 16384 || + (modulus_length_bits % 8) != 0) { + return Status::ErrorGenerateRsaUnsupportedModulus(); + } + unsigned int public_exponent = 0; - unsigned int modulus_length_bits = 0; - status = - GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits); - if (status.IsError()) - return status; + if (!BigIntegerToUint(params->publicExponent().data(), + params->publicExponent().size(), &public_exponent)) { + return Status::ErrorGenerateKeyPublicExponent(); + } + + // OpenSSL hangs when given bad public exponents. Use a whitelist. + if (public_exponent != 3 && public_exponent != 65537) + return Status::ErrorGenerateKeyPublicExponent(); crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); @@ -301,9 +433,12 @@ Status RsaHashedAlgorithm::ImportKeyJwk( return status; // Once the key type is known, verify the usages. - status = CheckKeyCreationUsages( - jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, - usages, !jwk.is_private_key); + if (jwk.is_private_key) { + status = CheckPrivateKeyCreationUsages(all_private_key_usages_, usages); + } else { + status = CheckPublicKeyCreationUsages(all_public_key_usages_, usages); + } + if (status.IsError()) return status; @@ -317,7 +452,10 @@ Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { if (key.type() != blink::WebCryptoKeyTypePrivate) return Status::ErrorUnexpectedKeyType(); - *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + // This relies on the fact that PKCS8 formatted data was already + // associated with the key during its creation (used by + // structured clone). + *buffer = GetSerializedKeyData(key); return Status::Success(); } @@ -325,7 +463,10 @@ Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { if (key.type() != blink::WebCryptoKeyTypePublic) return Status::ErrorUnexpectedKeyType(); - *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); + // This relies on the fact that SPKI formatted data was already + // associated with the key during its creation (used by + // structured clone). + *buffer = GetSerializedKeyData(key); return Status::Success(); } @@ -333,7 +474,7 @@ Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); + EVP_PKEY* pkey = GetEVP_PKEY(key); crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey)); if (!rsa.get()) return Status::ErrorUnexpected(); @@ -344,36 +485,34 @@ Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, return Status::ErrorUnexpected(); switch (key.type()) { - case blink::WebCryptoKeyTypePublic: - WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), - CryptoData(BIGNUMToVector(rsa->e)), jwk_algorithm, - key.extractable(), key.usages(), buffer); + case blink::WebCryptoKeyTypePublic: { + JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA"); + writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n))); + writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e))); + writer.ToJson(buffer); return Status::Success(); - case blink::WebCryptoKeyTypePrivate: - WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), - CryptoData(BIGNUMToVector(rsa->e)), - CryptoData(BIGNUMToVector(rsa->d)), - CryptoData(BIGNUMToVector(rsa->p)), - CryptoData(BIGNUMToVector(rsa->q)), - CryptoData(BIGNUMToVector(rsa->dmp1)), - CryptoData(BIGNUMToVector(rsa->dmq1)), - CryptoData(BIGNUMToVector(rsa->iqmp)), - jwk_algorithm, key.extractable(), key.usages(), - buffer); + } + case blink::WebCryptoKeyTypePrivate: { + JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA"); + writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n))); + writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e))); + writer.SetBytes("d", CryptoData(BIGNUMToVector(rsa->d))); + // Although these are "optional" in the JWA, WebCrypto spec requires them + // to be emitted. + writer.SetBytes("p", CryptoData(BIGNUMToVector(rsa->p))); + writer.SetBytes("q", CryptoData(BIGNUMToVector(rsa->q))); + writer.SetBytes("dp", CryptoData(BIGNUMToVector(rsa->dmp1))); + writer.SetBytes("dq", CryptoData(BIGNUMToVector(rsa->dmq1))); + writer.SetBytes("qi", CryptoData(BIGNUMToVector(rsa->iqmp))); + writer.ToJson(buffer); return Status::Success(); + } default: return Status::ErrorUnexpected(); } } -Status RsaHashedAlgorithm::SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const { - key_data->assign(AsymKeyOpenSsl::Cast(key)->serialized_key_data()); - return Status::Success(); -} - // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763 Status RsaHashedAlgorithm::DeserializeKeyForClone( const blink::WebCryptoKeyAlgorithm& algorithm, @@ -382,11 +521,12 @@ Status RsaHashedAlgorithm::DeserializeKeyForClone( blink::WebCryptoKeyUsageMask usages, const CryptoData& key_data, blink::WebCryptoKey* key) const { - blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( - algorithm.id(), algorithm.rsaHashedParams()->hash().id()); + blink::WebCryptoAlgorithm import_algorithm = + SynthesizeImportAlgorithmForClone(algorithm); Status status; + // The serialized data will be either SPKI or PKCS8 formatted. switch (type) { case blink::WebCryptoKeyTypePublic: status = diff --git a/chromium/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h b/chromium/components/webcrypto/algorithms/rsa.h index 4c0d44b810d..c0c919b59b5 100644 --- a/chromium/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h +++ b/chromium/components/webcrypto/algorithms/rsa.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_RSA_HASHED_ALGORITHM_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_RSA_HASHED_ALGORITHM_OPENSSL_H_ +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_H_ #include "components/webcrypto/algorithm_implementation.h" @@ -69,10 +69,6 @@ class RsaHashedAlgorithm : public AlgorithmImplementation { Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector<uint8_t>* buffer) const override; - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override; - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, @@ -87,4 +83,4 @@ class RsaHashedAlgorithm : public AlgorithmImplementation { } // namespace webcrypto -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_RSA_HASHED_ALGORITHM_OPENSSL_H_ +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_H_ diff --git a/chromium/components/webcrypto/openssl/rsa_oaep_openssl.cc b/chromium/components/webcrypto/algorithms/rsa_oaep.cc index 1a43f725bc8..5a697095694 100644 --- a/chromium/components/webcrypto/openssl/rsa_oaep_openssl.cc +++ b/chromium/components/webcrypto/algorithms/rsa_oaep.cc @@ -5,10 +5,10 @@ #include <openssl/evp.h> #include "base/stl_util.h" +#include "components/webcrypto/algorithms/rsa.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" @@ -41,9 +41,8 @@ Status CommonEncryptDecrypt(InitFunc init_func, std::vector<uint8_t>* buffer) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); - const EVP_MD* digest = - GetDigest(key.algorithm().rsaHashedParams()->hash().id()); + EVP_PKEY* pkey = GetEVP_PKEY(key); + const EVP_MD* digest = GetDigest(key.algorithm().rsaHashedParams()->hash()); if (!digest) return Status::ErrorUnsupported(); @@ -139,8 +138,8 @@ class RsaOaepImplementation : public RsaHashedAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformRsaOaepImplementation() { - return new RsaOaepImplementation; +scoped_ptr<AlgorithmImplementation> CreateRsaOaepImplementation() { + return make_scoped_ptr(new RsaOaepImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc new file mode 100644 index 00000000000..32cd1353b4e --- /dev/null +++ b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc @@ -0,0 +1,504 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Creates an RSA-OAEP algorithm +blink::WebCryptoAlgorithm CreateRsaOaepAlgorithm( + const std::vector<uint8_t>& label) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdRsaOaep, + new blink::WebCryptoRsaOaepParams( + !label.empty(), vector_as_array(&label), + static_cast<unsigned int>(label.size()))); +} + +scoped_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() { + scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); + jwk->SetString("kty", "RSA"); + jwk->SetString("n", + Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex))); + jwk->SetString("e", + Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex))); + return jwk.Pass(); +} + +class WebCryptoRsaOaepTest : public WebCryptoTestBase {}; + +// Import a PKCS#8 private key that uses RSAPrivateKey with the +// id-rsaEncryption OID. +TEST_F(WebCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) { + blink::WebCryptoKey private_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageDecrypt, &private_key)); +} + +TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) { + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + + blink::WebCryptoKey public_key; + ASSERT_EQ( + Status::Success(), + ImportKeyJwkFromDict(*jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); +} + +TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) { + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + jwk->SetString("alg", "RSA-OAEP"); + + blink::WebCryptoKey public_key; + ASSERT_EQ( + Status::Success(), + ImportKeyJwkFromDict(*jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); +} + +TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) { + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + jwk->SetString("alg", "RSA-OAEP-512"); + + blink::WebCryptoKey public_key; + ASSERT_EQ( + Status::ErrorJwkAlgorithmInconsistent(), + ImportKeyJwkFromDict(*jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); +} + +TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) { + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + jwk->SetString("kty", "oct"); + jwk->SetString("alg", "RSA-OAEP"); + + blink::WebCryptoKey public_key; + ASSERT_EQ( + Status::ErrorJwkUnexpectedKty("RSA"), + ImportKeyJwkFromDict(*jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); +} + +TEST_F(WebCryptoRsaOaepTest, ExportPublicJwk) { + struct TestData { + blink::WebCryptoAlgorithmId hash_alg; + const char* expected_jwk_alg; + } kTestData[] = {{blink::WebCryptoAlgorithmIdSha1, "RSA-OAEP"}, + {blink::WebCryptoAlgorithmIdSha256, "RSA-OAEP-256"}, + {blink::WebCryptoAlgorithmIdSha384, "RSA-OAEP-384"}, + {blink::WebCryptoAlgorithmIdSha512, "RSA-OAEP-512"}}; + for (size_t i = 0; i < arraysize(kTestData); ++i) { + const TestData& test_data = kTestData[i]; + SCOPED_TRACE(test_data.expected_jwk_alg); + + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + jwk->SetString("alg", test_data.expected_jwk_alg); + + // Import the key in a known-good format + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict( + *jwk.get(), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, test_data.hash_alg), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); + + // Now export the key as JWK and verify its contents + std::vector<uint8_t> jwk_data; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk_data)); + EXPECT_TRUE(VerifyPublicJwk(jwk_data, test_data.expected_jwk_alg, + kPublicKeyModulusHex, kPublicKeyExponentHex, + blink::WebCryptoKeyUsageEncrypt)); + } +} + +TEST_F(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("rsa_oaep.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + base::DictionaryValue* test = NULL; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoAlgorithm digest_algorithm = + GetDigestAlgorithm(test, "hash"); + ASSERT_FALSE(digest_algorithm.isNull()); + std::vector<uint8_t> public_key_der = + GetBytesFromHexString(test, "public_key"); + std::vector<uint8_t> private_key_der = + GetBytesFromHexString(test, "private_key"); + std::vector<uint8_t> ciphertext = GetBytesFromHexString(test, "ciphertext"); + std::vector<uint8_t> plaintext = GetBytesFromHexString(test, "plaintext"); + std::vector<uint8_t> label = GetBytesFromHexString(test, "label"); + + blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, digest_algorithm.id()); + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( + public_key_der, private_key_der, import_algorithm, false, + blink::WebCryptoKeyUsageEncrypt, blink::WebCryptoKeyUsageDecrypt, + &public_key, &private_key)); + + blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); + std::vector<uint8_t> decrypted_data; + ASSERT_EQ(Status::Success(), + Decrypt(op_algorithm, private_key, CryptoData(ciphertext), + &decrypted_data)); + EXPECT_BYTES_EQ(plaintext, decrypted_data); + std::vector<uint8_t> encrypted_data; + ASSERT_EQ(Status::Success(), + Encrypt(op_algorithm, public_key, CryptoData(plaintext), + &encrypted_data)); + std::vector<uint8_t> redecrypted_data; + ASSERT_EQ(Status::Success(), + Decrypt(op_algorithm, private_key, CryptoData(encrypted_data), + &redecrypted_data)); + EXPECT_BYTES_EQ(plaintext, redecrypted_data); + } +} + +TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) { + const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha1; + const size_t kHashSize = 20; + + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict( + *jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, kHash), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); + + // The maximum size of an encrypted message is: + // modulus length + // - 1 (leading octet) + // - hash size (maskedSeed) + // - hash size (lHash portion of maskedDB) + // - 1 (at least one octet for the padding string) + size_t kMaxMessageSize = (kModulusLengthBits / 8) - 2 - (2 * kHashSize); + + // The label has no influence on the maximum message size. For simplicity, + // use the empty string. + std::vector<uint8_t> label; + blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); + + // Test that a message just before the boundary succeeds. + std::string large_message; + large_message.resize(kMaxMessageSize - 1, 'A'); + + std::vector<uint8_t> ciphertext; + ASSERT_EQ(Status::Success(), Encrypt(op_algorithm, public_key, + CryptoData(large_message), &ciphertext)); + + // Test that a message at the boundary succeeds. + large_message.resize(kMaxMessageSize, 'A'); + ciphertext.clear(); + + ASSERT_EQ(Status::Success(), Encrypt(op_algorithm, public_key, + CryptoData(large_message), &ciphertext)); + + // Test that a message greater than the largest size fails. + large_message.resize(kMaxMessageSize + 1, 'A'); + ciphertext.clear(); + + ASSERT_EQ(Status::OperationError(), + Encrypt(op_algorithm, public_key, CryptoData(large_message), + &ciphertext)); +} + +// Ensures that if the selected hash algorithm for the RSA-OAEP message is too +// large, then it is rejected, independent of the actual message to be +// encrypted. +// For example, a 1024-bit RSA key is too small to accomodate a message that +// uses OAEP with SHA-512, since it requires 1040 bits to encode +// (2 * hash size + 2 padding bytes). +TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeDigestFails) { + const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha512; + + scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); + + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict( + *jwk.get(), CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, kHash), + true, blink::WebCryptoKeyUsageEncrypt, &public_key)); + + // The label has no influence on the maximum message size. For simplicity, + // use the empty string. + std::vector<uint8_t> label; + blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); + + std::string small_message("A"); + std::vector<uint8_t> ciphertext; + // This is an operation error, as the internal consistency checking of the + // algorithm parameters is up to the implementation. + ASSERT_EQ(Status::OperationError(), + Encrypt(op_algorithm, public_key, CryptoData(small_message), + &ciphertext)); +} + +TEST_F(WebCryptoRsaOaepTest, DecryptWithLargeMessageFails) { + blink::WebCryptoKey private_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageDecrypt, &private_key)); + + // The label has no influence on the maximum message size. For simplicity, + // use the empty string. + std::vector<uint8_t> label; + blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); + + std::string large_dummy_message(kModulusLengthBits / 8, 'A'); + std::vector<uint8_t> plaintext; + + ASSERT_EQ(Status::OperationError(), + Decrypt(op_algorithm, private_key, CryptoData(large_dummy_message), + &plaintext)); +} + +TEST_F(WebCryptoRsaOaepTest, WrapUnwrapRawKey) { + blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, + &public_key, &private_key)); + + std::vector<uint8_t> label; + blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); + + const std::string key_hex = "000102030405060708090A0B0C0D0E0F"; + const blink::WebCryptoAlgorithm key_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); + + blink::WebCryptoKey key = + ImportSecretKeyFromRaw(HexStringToBytes(key_hex), key_algorithm, + blink::WebCryptoKeyUsageEncrypt); + ASSERT_FALSE(key.isNull()); + + std::vector<uint8_t> wrapped_key; + ASSERT_EQ(Status::Success(), + WrapKey(blink::WebCryptoKeyFormatRaw, key, public_key, + wrapping_algorithm, &wrapped_key)); + + // Verify that |wrapped_key| can be decrypted and yields the key data. + // Because |private_key| supports both decrypt and unwrap, this is valid. + std::vector<uint8_t> decrypted_key; + ASSERT_EQ(Status::Success(), + Decrypt(wrapping_algorithm, private_key, CryptoData(wrapped_key), + &decrypted_key)); + EXPECT_BYTES_EQ_HEX(key_hex, decrypted_key); + + // Now attempt to unwrap the key, which should also decrypt the data. + blink::WebCryptoKey unwrapped_key; + ASSERT_EQ(Status::Success(), + UnwrapKey(blink::WebCryptoKeyFormatRaw, CryptoData(wrapped_key), + private_key, wrapping_algorithm, key_algorithm, true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); + ASSERT_FALSE(unwrapped_key.isNull()); + + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); + EXPECT_BYTES_EQ_HEX(key_hex, raw_key); +} + +TEST_F(WebCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { + // The public and private portions of a 2048-bit RSA key with the + // id-rsaEncryption OID + const char kPublicKey2048SpkiDerHex[] = + "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c5d8ce" + "137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b300c6a6c9764" + "f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448e7183a3a68" + "e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872458d1b1e2f" + "7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34ba17bc5d08" + "a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea9893652d02fc606" + "36f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d733711c89ca" + "749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b557c16615d" + "5d0203010001"; + const char kPrivateKey2048Pkcs8DerHex[] = + "308204bd020100300d06092a864886f70d0101010500048204a7308204a3020100028201" + "0100c5d8ce137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b30" + "0c6a6c9764f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448" + "e7183a3a68e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872" + "458d1b1e2f7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34" + "ba17bc5d08a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea98936" + "52d02fc60636f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d7" + "33711c89ca749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b" + "557c16615d5d02030100010282010074b70feb41a0b0fcbc207670400556c9450042ede3" + "d4383fb1ce8f3558a6d4641d26dd4c333fa4db842d2b9cf9d2354d3e16ad027a9f682d8c" + "f4145a1ad97b9edcd8a41c402bd9d8db10f62f43df854cdccbbb2100834f083f53ed6d42" + "b1b729a59072b004a4e945fc027db15e9c121d1251464d320d4774d5732df6b3dbf751f4" + "9b19c9db201e19989c883bbaad5333db47f64f6f7a95b8d4936b10d945aa3f794cfaab62" + "e7d47686129358914f3b8085f03698a650ab5b8c7e45813f2b0515ec05b6e5195b6a7c2a" + "0d36969745f431ded4fd059f6aa361a4649541016d356297362b778e90f077d48815b339" + "ec6f43aba345df93e67fcb6c2cb5b4544e9be902818100e9c90abe5f9f32468c5b6d630c" + "54a4d7d75e29a72cf792f21e242aac78fd7995c42dfd4ae871d2619ff7096cb05baa78e3" + "23ecab338401a8059adf7a0d8be3b21edc9a9c82c5605634a2ec81ec053271721351868a" + "4c2e50c689d7cef94e31ff23658af5843366e2b289c5bf81d72756a7b93487dd8770d69c" + "1f4e089d6d89f302818100d8a58a727c4e209132afd9933b98c89aca862a01cc0be74133" + "bee517909e5c379e526895ac4af11780c1fe91194c777c9670b6423f0f5a32fd7691a622" + "113eef4bed2ef863363a335fd55b0e75088c582437237d7f3ed3f0a643950237bc6e6277" + "ccd0d0a1b4170aa1047aa7ffa7c8c54be10e8c7327ae2e0885663963817f6f02818100e5" + "aed9ba4d71b7502e6748a1ce247ecb7bd10c352d6d9256031cdf3c11a65e44b0b7ca2945" + "134671195af84c6b3bb3d10ebf65ae916f38bd5dbc59a0ad1c69b8beaf57cb3a8335f19b" + "c7117b576987b48331cd9fd3d1a293436b7bb5e1a35c6560de4b5688ea834367cb0997eb" + "b578f59ed4cb724c47dba94d3b484c1876dcd70281807f15bc7d2406007cac2b138a96af" + "2d1e00276b84da593132c253fcb73212732dfd25824c2a615bc3d9b7f2c8d2fa542d3562" + "b0c7738e61eeff580a6056239fb367ea9e5efe73d4f846033602e90c36a78db6fa8ea792" + "0769675ec58e237bd994d189c8045a96f5dd3a4f12547257ce224e3c9af830a4da3c0eab" + "9227a0035ae9028180067caea877e0b23090fc689322b71fbcce63d6596e66ab5fcdbaa0" + "0d49e93aba8effb4518c2da637f209028401a68f344865b4956b032c69acde51d29177ca" + "3db99fdbf5e74848ed4fa7bdfc2ebb60e2aaa5354770a763e1399ab7a2099762d525fea0" + "37f3e1972c45a477e66db95c9609bb27f862700ef93379930786cf751b"; + blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( + HexStringToBytes(kPublicKey2048SpkiDerHex), + HexStringToBytes(kPrivateKey2048Pkcs8DerHex), import_algorithm, false, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, + &public_key, &private_key)); + + std::vector<uint8_t> label; + blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); + + const std::string key_hex = "000102030405060708090a0b0c0d0e0f"; + const blink::WebCryptoAlgorithm key_algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); + + blink::WebCryptoKey key = + ImportSecretKeyFromRaw(HexStringToBytes(key_hex), key_algorithm, + blink::WebCryptoKeyUsageEncrypt); + ASSERT_FALSE(key.isNull()); + + std::vector<uint8_t> wrapped_key; + ASSERT_EQ(Status::Success(), + WrapKey(blink::WebCryptoKeyFormatJwk, key, public_key, + wrapping_algorithm, &wrapped_key)); + + // Verify that |wrapped_key| can be decrypted and yields a valid JWK object. + // Because |private_key| supports both decrypt and unwrap, this is valid. + std::vector<uint8_t> decrypted_jwk; + ASSERT_EQ(Status::Success(), + Decrypt(wrapping_algorithm, private_key, CryptoData(wrapped_key), + &decrypted_jwk)); + EXPECT_TRUE(VerifySecretJwk(decrypted_jwk, "A128CBC", key_hex, + blink::WebCryptoKeyUsageEncrypt)); + + // Now attempt to unwrap the key, which should also decrypt the data. + blink::WebCryptoKey unwrapped_key; + ASSERT_EQ(Status::Success(), + UnwrapKey(blink::WebCryptoKeyFormatJwk, CryptoData(wrapped_key), + private_key, wrapping_algorithm, key_algorithm, true, + blink::WebCryptoKeyUsageEncrypt, &unwrapped_key)); + ASSERT_FALSE(unwrapped_key.isNull()); + + std::vector<uint8_t> raw_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); + EXPECT_BYTES_EQ_HEX(key_hex, raw_key); +} + +TEST_F(WebCryptoRsaOaepTest, ImportExportJwkRsaPublicKey) { + struct TestCase { + const blink::WebCryptoAlgorithmId hash; + const blink::WebCryptoKeyUsageMask usage; + const char* const jwk_alg; + }; + const TestCase kTests[] = {{blink::WebCryptoAlgorithmIdSha1, + blink::WebCryptoKeyUsageEncrypt, + "RSA-OAEP"}, + {blink::WebCryptoAlgorithmIdSha256, + blink::WebCryptoKeyUsageEncrypt, + "RSA-OAEP-256"}, + {blink::WebCryptoAlgorithmIdSha384, + blink::WebCryptoKeyUsageEncrypt, + "RSA-OAEP-384"}, + {blink::WebCryptoAlgorithmIdSha512, + blink::WebCryptoKeyUsageEncrypt, + "RSA-OAEP-512"}}; + + for (size_t test_index = 0; test_index < arraysize(kTests); ++test_index) { + SCOPED_TRACE(test_index); + const TestCase& test = kTests[test_index]; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, + test.hash); + + // Import the spki to create a public key + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + import_algorithm, true, test.usage, &public_key)); + + // Export the public key as JWK and verify its contents + std::vector<uint8_t> jwk; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); + EXPECT_TRUE(VerifyPublicJwk(jwk, test.jwk_alg, kPublicKeyModulusHex, + kPublicKeyExponentHex, test.usage)); + + // Import the JWK back in to create a new key + blink::WebCryptoKey public_key2; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk), + import_algorithm, true, test.usage, &public_key2)); + ASSERT_TRUE(public_key2.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); + EXPECT_TRUE(public_key2.extractable()); + EXPECT_EQ(import_algorithm.id(), public_key2.algorithm().id()); + + // TODO(eroman): Export the SPKI and verify matches. + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/rsa_pss_openssl.cc b/chromium/components/webcrypto/algorithms/rsa_pss.cc index 68c288f6366..14166bd9a13 100644 --- a/chromium/components/webcrypto/openssl/rsa_pss_openssl.cc +++ b/chromium/components/webcrypto/algorithms/rsa_pss.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" -#include "components/webcrypto/openssl/rsa_sign_openssl.h" +#include "components/webcrypto/algorithms/rsa.h" +#include "components/webcrypto/algorithms/rsa_sign.h" #include "components/webcrypto/status.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" @@ -53,8 +53,8 @@ class RsaPssImplementation : public RsaHashedAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformRsaPssImplementation() { - return new RsaPssImplementation; +scoped_ptr<AlgorithmImplementation> CreateRsaPssImplementation() { + return make_scoped_ptr(new RsaPssImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc new file mode 100644 index 00000000000..e8b2bfd5b55 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc @@ -0,0 +1,230 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +blink::WebCryptoAlgorithm CreateRsaPssAlgorithm( + unsigned int salt_length_bytes) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdRsaPss, + new blink::WebCryptoRsaPssParams(salt_length_bytes)); +} + +class WebCryptoRsaPssTest : public WebCryptoTestBase {}; + +// Test that no two RSA-PSS signatures are identical, when using a non-zero +// lengthed salt. +TEST_F(WebCryptoRsaPssTest, SignIsRandom) { + // Import public/private key pair. + blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); + blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); + + ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, + &public_key, &private_key); + + // Use a 20-byte length salt. + blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(20); + + // Some random message to sign. + std::vector<uint8_t> message = HexStringToBytes(kPublicKeySpkiDerHex); + + // Sign twice. + std::vector<uint8_t> signature1; + std::vector<uint8_t> signature2; + + ASSERT_EQ(Status::Success(), + Sign(params, private_key, CryptoData(message), &signature1)); + ASSERT_EQ(Status::Success(), + Sign(params, private_key, CryptoData(message), &signature2)); + + // The signatures will be different because of the salt. + EXPECT_NE(CryptoData(signature1), CryptoData(signature2)); + + // However both signatures should work when verifying. + bool is_match = false; + + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(signature1), + CryptoData(message), &is_match)); + EXPECT_TRUE(is_match); + + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(signature2), + CryptoData(message), &is_match)); + EXPECT_TRUE(is_match); + + // Corrupt the signature and verification must fail. + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(Corrupted(signature2)), + CryptoData(message), &is_match)); + EXPECT_FALSE(is_match); +} + +// Try signing and verifying when the salt length is 0. The signature in this +// case is not random. +TEST_F(WebCryptoRsaPssTest, SignVerifyNoSalt) { + // Import public/private key pair. + blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); + blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); + + ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, + &public_key, &private_key); + + // Zero-length salt. + blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(0); + + // Some random message to sign. + std::vector<uint8_t> message = HexStringToBytes(kPublicKeySpkiDerHex); + + // Sign twice. + std::vector<uint8_t> signature1; + std::vector<uint8_t> signature2; + + ASSERT_EQ(Status::Success(), + Sign(params, private_key, CryptoData(message), &signature1)); + ASSERT_EQ(Status::Success(), + Sign(params, private_key, CryptoData(message), &signature2)); + + // The signatures will be the same this time. + EXPECT_EQ(CryptoData(signature1), CryptoData(signature2)); + + // Make sure that verification works. + bool is_match = false; + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(signature1), + CryptoData(message), &is_match)); + EXPECT_TRUE(is_match); + + // Corrupt the signature and verification must fail. + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(Corrupted(signature2)), + CryptoData(message), &is_match)); + EXPECT_FALSE(is_match); +} + +TEST_F(WebCryptoRsaPssTest, SignEmptyMessage) { + // Import public/private key pair. + blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); + blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); + + ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, + &public_key, &private_key); + + blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(20); + std::vector<uint8_t> message; // Empty message. + std::vector<uint8_t> signature; + + ASSERT_EQ(Status::Success(), + Sign(params, private_key, CryptoData(message), &signature)); + + // Make sure that verification works. + bool is_match = false; + ASSERT_EQ(Status::Success(), Verify(params, public_key, CryptoData(signature), + CryptoData(message), &is_match)); + EXPECT_TRUE(is_match); + + // Corrupt the signature and verification must fail. + ASSERT_EQ(Status::Success(), + Verify(params, public_key, CryptoData(Corrupted(signature)), + CryptoData(message), &is_match)); + EXPECT_FALSE(is_match); +} + +// Iterate through known answers and test verification. +// * Verify over original message should succeed +// * Verify over corrupted message should fail +// * Verification with corrupted signature should fail +TEST_F(WebCryptoRsaPssTest, VerifyKnownAnswer) { + scoped_ptr<base::DictionaryValue> test_data; + ASSERT_TRUE(ReadJsonTestFileToDictionary("rsa_pss.json", &test_data)); + + const base::DictionaryValue* keys_dict = NULL; + ASSERT_TRUE(test_data->GetDictionary("keys", &keys_dict)); + + const base::ListValue* tests = NULL; + ASSERT_TRUE(test_data->GetList("tests", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash"); + + std::string key_name; + ASSERT_TRUE(test->GetString("key", &key_name)); + + // Import the public key. + blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); + std::vector<uint8_t> spki_bytes = + GetBytesFromHexString(keys_dict, key_name); + + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(spki_bytes), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaPss, hash.id()), + true, blink::WebCryptoKeyUsageVerify, &public_key)); + + int saltLength; + ASSERT_TRUE(test->GetInteger("saltLength", &saltLength)); + + std::vector<uint8_t> message = GetBytesFromHexString(test, "message"); + std::vector<uint8_t> signature = GetBytesFromHexString(test, "signature"); + + // Test that verification returns true when it should. + bool is_match = false; + ASSERT_EQ(Status::Success(), + Verify(CreateRsaPssAlgorithm(saltLength), public_key, + CryptoData(signature), CryptoData(message), &is_match)); + EXPECT_TRUE(is_match); + + // Corrupt the message and make sure that verification fails. + ASSERT_EQ(Status::Success(), + Verify(CreateRsaPssAlgorithm(saltLength), public_key, + CryptoData(signature), CryptoData(Corrupted(message)), + &is_match)); + EXPECT_FALSE(is_match); + + // Corrupt the signature and make sure that verification fails. + ASSERT_EQ(Status::Success(), + Verify(CreateRsaPssAlgorithm(saltLength), public_key, + CryptoData(Corrupted(signature)), CryptoData(message), + &is_match)); + EXPECT_FALSE(is_match); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/rsa_sign_openssl.cc b/chromium/components/webcrypto/algorithms/rsa_sign.cc index 41d14e87d23..5c038b543d8 100644 --- a/chromium/components/webcrypto/openssl/rsa_sign_openssl.cc +++ b/chromium/components/webcrypto/algorithms/rsa_sign.cc @@ -4,10 +4,10 @@ #include "base/numerics/safe_math.h" #include "base/stl_util.h" +#include "components/webcrypto/algorithms/rsa_sign.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/key_openssl.h" -#include "components/webcrypto/openssl/rsa_sign_openssl.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" @@ -22,9 +22,9 @@ namespace { Status GetPKeyAndDigest(const blink::WebCryptoKey& key, EVP_PKEY** pkey, const EVP_MD** digest) { - *pkey = AsymKeyOpenSsl::Cast(key)->key(); + *pkey = GetEVP_PKEY(key); - *digest = GetDigest(key.algorithm().rsaHashedParams()->hash().id()); + *digest = GetDigest(key.algorithm().rsaHashedParams()->hash()); if (!*digest) return Status::ErrorUnsupported(); diff --git a/chromium/components/webcrypto/openssl/rsa_sign_openssl.h b/chromium/components/webcrypto/algorithms/rsa_sign.h index 9aacfb18600..2e3f09200cf 100644 --- a/chromium/components/webcrypto/openssl/rsa_sign_openssl.h +++ b/chromium/components/webcrypto/algorithms/rsa_sign.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_SIGN_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_SIGN_H_ #include <stdint.h> @@ -37,4 +37,4 @@ Status RsaVerify(const blink::WebCryptoKey& key, } // namespace webcrypto -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_RSA_SIGN_OPENSSL_H_ +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_RSA_SIGN_H_ diff --git a/chromium/components/webcrypto/openssl/rsa_ssa_openssl.cc b/chromium/components/webcrypto/algorithms/rsa_ssa.cc index 257a6a9d97e..54eefbb8a52 100644 --- a/chromium/components/webcrypto/openssl/rsa_ssa_openssl.cc +++ b/chromium/components/webcrypto/algorithms/rsa_ssa.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" -#include "components/webcrypto/openssl/rsa_sign_openssl.h" +#include "components/webcrypto/algorithms/rsa.h" +#include "components/webcrypto/algorithms/rsa_sign.h" #include "components/webcrypto/status.h" namespace webcrypto { @@ -50,8 +50,8 @@ class RsaSsaImplementation : public RsaHashedAlgorithm { } // namespace -AlgorithmImplementation* CreatePlatformRsaSsaImplementation() { - return new RsaSsaImplementation; +scoped_ptr<AlgorithmImplementation> CreateRsaSsaImplementation() { + return make_scoped_ptr(new RsaSsaImplementation); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc new file mode 100644 index 00000000000..31cc967b386 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc @@ -0,0 +1,1020 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Helper for ImportJwkRsaFailures. Restores the JWK JSON +// dictionary to a good state +void RestoreJwkRsaDictionary(base::DictionaryValue* dict) { + dict->Clear(); + dict->SetString("kty", "RSA"); + dict->SetString("alg", "RS256"); + dict->SetString("use", "sig"); + dict->SetBoolean("ext", false); + dict->SetString( + "n", + "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" + "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" + "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); + dict->SetString("e", "AQAB"); +} + +class WebCryptoRsaSsaTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) { + // Passing case: Import a valid RSA key in SPKI format. + blink::WebCryptoKey key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageVerify, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); + EXPECT_TRUE(key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); + EXPECT_EQ(kModulusLengthBits, + key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_BYTES_EQ_HEX( + "010001", + CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); + + // Failing case: Import RSA key but provide an inconsistent input algorithm. + EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageEncrypt, &key)); + + // Passing case: Export a previously imported RSA public key in SPKI format + // and compare to original data. + std::vector<uint8_t> output; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); + EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, output); + + // Failing case: Try to export a previously imported RSA public key in raw + // format (not allowed for a public key). + EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &output)); + + // Failing case: Try to export a non-extractable key + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + false, blink::WebCryptoKeyUsageVerify, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_FALSE(key.extractable()); + EXPECT_EQ(Status::ErrorKeyNotExtractable(), + ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); + + // TODO(eroman): Failing test: Import a SPKI with an unrecognized hash OID + // TODO(eroman): Failing test: Import a SPKI with invalid algorithm params + // TODO(eroman): Failing test: Import a SPKI with inconsistent parameters + // (e.g. SHA-1 in OID, SHA-256 in params) + // TODO(eroman): Failing test: Import a SPKI for RSA-SSA, but with params + // as OAEP/PSS +} + +TEST_F(WebCryptoRsaSsaTest, ImportExportPkcs8) { + // Passing case: Import a valid RSA key in PKCS#8 format. + blink::WebCryptoKey key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, key.type()); + EXPECT_TRUE(key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + key.algorithm().rsaHashedParams()->hash().id()); + EXPECT_EQ(kModulusLengthBits, + key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_BYTES_EQ_HEX( + "010001", + CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); + + std::vector<uint8_t> exported_key; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key)); + EXPECT_BYTES_EQ_HEX(kPrivateKeyPkcs8DerHex, exported_key); + + // Failing case: Import RSA key but provide an inconsistent input algorithm + // and usage. Several issues here: + // * AES-CBC doesn't support PKCS8 key format + // * AES-CBC doesn't support "sign" usage + EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, + blink::WebCryptoKeyUsageSign, &key)); +} + +// Tests JWK import and export by doing a roundtrip key conversion and ensuring +// it was lossless: +// +// PKCS8 --> JWK --> PKCS8 +TEST_F(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) { + blink::WebCryptoKey key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &key)); + + std::vector<uint8_t> exported_key_jwk; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_key_jwk)); + + // All of the optional parameters (p, q, dp, dq, qi) should be present in the + // output. + const char* expected_jwk = + "{\"alg\":\"RS1\",\"d\":\"M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" + "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" + "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU\",\"dp\":" + "\"KPoTk4ZVvh-" + "KFZy6ylpy6hkMMAieGc0nSlVvNsT24Z9VSzTAd3kEJ7vdjdPt4kSDKPOF2Bsw6OQ7L_-" + "gJ4YZeQ\",\"dq\":\"Gos485j6cSBJiY1_t57gp3ZoeRKZzfoJ78DlB6yyHtdDAe9b_Ui-" + "RV6utuFnglWCdYCo5OjhQVHRUQqCo_LnKQ\",\"e\":\"AQAB\",\"ext\":true,\"key_" + "ops\":[\"sign\"],\"kty\":\"RSA\",\"n\":" + "\"pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" + "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" + "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc\",\"p\":\"5-" + "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31WhU1vZs8w0Fg" + "s7bc0-2o5kQw\",\"q\":\"tp3KHPfU1-yB51uQ_MqHSrzeEj_" + "ScAGAqpBHm25I3o1n7ST58Z2FuidYdPVCzSDccj5pYzZKH5QlRSsmmmeZ_Q\",\"qi\":" + "\"JxVqukEm0kqB86Uoy_sn9WiG-" + "ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}"; + + ASSERT_EQ(CryptoData(std::string(expected_jwk)), + CryptoData(exported_key_jwk)); + + ASSERT_EQ( + Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(exported_key_jwk), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &key)); + + std::vector<uint8_t> exported_key_pkcs8; + ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, key, + &exported_key_pkcs8)); + + ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CryptoData(exported_key_pkcs8)); +} + +// Tests importing multiple RSA private keys from JWK, and then exporting to +// PKCS8. +// +// This is a regression test for http://crbug.com/378315, for which importing +// a sequence of keys from JWK could yield the wrong key. The first key would +// be imported correctly, however every key after that would actually import +// the first key. +TEST_F(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) { + scoped_ptr<base::ListValue> key_list; + ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); + + // For this test to be meaningful the keys MUST be kept alive before importing + // new keys. + std::vector<blink::WebCryptoKey> live_keys; + + for (size_t key_index = 0; key_index < key_list->GetSize(); ++key_index) { + SCOPED_TRACE(key_index); + + base::DictionaryValue* key_values; + ASSERT_TRUE(key_list->GetDictionary(key_index, &key_values)); + + // Get the JWK representation of the key. + base::DictionaryValue* key_jwk; + ASSERT_TRUE(key_values->GetDictionary("jwk", &key_jwk)); + + // Get the PKCS8 representation of the key. + std::string pkcs8_hex_string; + ASSERT_TRUE(key_values->GetString("pkcs8", &pkcs8_hex_string)); + std::vector<uint8_t> pkcs8_bytes = HexStringToBytes(pkcs8_hex_string); + + // Get the modulus length for the key. + int modulus_length_bits = 0; + ASSERT_TRUE(key_values->GetInteger("modulusLength", &modulus_length_bits)); + + blink::WebCryptoKey private_key; + + // Import the key from JWK. + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict( + *key_jwk, CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageSign, &private_key)); + + live_keys.push_back(private_key); + + EXPECT_EQ( + modulus_length_bits, + static_cast<int>( + private_key.algorithm().rsaHashedParams()->modulusLengthBits())); + + // Export to PKCS8 and verify that it matches expectation. + std::vector<uint8_t> exported_key_pkcs8; + ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, + private_key, &exported_key_pkcs8)); + + EXPECT_BYTES_EQ(pkcs8_bytes, exported_key_pkcs8); + } +} + +// Import an RSA private key using JWK. Next import a JWK containing the same +// modulus, but mismatched parameters for the rest. It should NOT be possible +// that the second import retrieves the first key. See http://crbug.com/378315 +// for how that could happen. +TEST_F(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) { + scoped_ptr<base::ListValue> key_list; + ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); + + // Import a 1024-bit private key. + base::DictionaryValue* key1_props; + ASSERT_TRUE(key_list->GetDictionary(1, &key1_props)); + base::DictionaryValue* key1_jwk; + ASSERT_TRUE(key1_props->GetDictionary("jwk", &key1_jwk)); + + blink::WebCryptoKey key1; + ASSERT_EQ(Status::Success(), + ImportKeyJwkFromDict(*key1_jwk, + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageSign, &key1)); + + ASSERT_EQ(1024u, key1.algorithm().rsaHashedParams()->modulusLengthBits()); + + // Construct a JWK using the modulus of key1, but all the other fields from + // another key (also a 1024-bit private key). + base::DictionaryValue* key2_props; + ASSERT_TRUE(key_list->GetDictionary(5, &key2_props)); + base::DictionaryValue* key2_jwk; + ASSERT_TRUE(key2_props->GetDictionary("jwk", &key2_jwk)); + std::string modulus; + key1_jwk->GetString("n", &modulus); + key2_jwk->SetString("n", modulus); + + // This should fail, as the n,e,d parameters are not consistent. It MUST NOT + // somehow return the key created earlier. + blink::WebCryptoKey key2; + ASSERT_EQ(Status::OperationError(), + ImportKeyJwkFromDict(*key2_jwk, + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageSign, &key2)); +} + +TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsa) { + // Note: using unrealistic short key lengths here to avoid bogging down tests. + + // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha256) + const unsigned int modulus_length = 256; + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, public_exponent); + bool extractable = true; + const blink::WebCryptoKeyUsageMask public_usages = + blink::WebCryptoKeyUsageVerify; + const blink::WebCryptoKeyUsageMask private_usages = + blink::WebCryptoKeyUsageSign; + const blink::WebCryptoKeyUsageMask usages = public_usages | private_usages; + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, + &public_key, &private_key)); + ASSERT_FALSE(public_key.isNull()); + ASSERT_FALSE(private_key.isNull()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); + EXPECT_EQ(modulus_length, + public_key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_EQ(modulus_length, + private_key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + public_key.algorithm().rsaHashedParams()->hash().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + private_key.algorithm().rsaHashedParams()->hash().id()); + EXPECT_TRUE(public_key.extractable()); + EXPECT_EQ(extractable, private_key.extractable()); + EXPECT_EQ(public_usages, public_key.usages()); + EXPECT_EQ(private_usages, private_key.usages()); + + // Try exporting the generated key pair, and then re-importing to verify that + // the exported data was valid. + std::vector<uint8_t> public_key_spki; + EXPECT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatSpki, + public_key, &public_key_spki)); + + public_key = blink::WebCryptoKey::createNull(); + ASSERT_EQ( + Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(public_key_spki), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, public_usages, &public_key)); + EXPECT_EQ(modulus_length, + public_key.algorithm().rsaHashedParams()->modulusLengthBits()); + + std::vector<uint8_t> private_key_pkcs8; + EXPECT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, + private_key, &private_key_pkcs8)); + private_key = blink::WebCryptoKey::createNull(); + ASSERT_EQ( + Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(private_key_pkcs8), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, private_usages, &private_key)); + EXPECT_EQ(modulus_length, + private_key.algorithm().rsaHashedParams()->modulusLengthBits()); + + // Fail with bad modulus. + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, 0, public_exponent); + EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), + GenerateKeyPair(algorithm, extractable, usages, &public_key, + &private_key)); + + // Fail with bad exponent: larger than unsigned long. + unsigned int exponent_length = sizeof(unsigned long) + 1; // NOLINT + const std::vector<uint8_t> long_exponent(exponent_length, 0x01); + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, long_exponent); + EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), + GenerateKeyPair(algorithm, extractable, usages, &public_key, + &private_key)); + + // Fail with bad exponent: empty. + const std::vector<uint8_t> empty_exponent; + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, empty_exponent); + EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), + GenerateKeyPair(algorithm, extractable, usages, &public_key, + &private_key)); + + // Fail with bad exponent: all zeros. + std::vector<uint8_t> exponent_with_leading_zeros(15, 0x00); + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, + exponent_with_leading_zeros); + EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), + GenerateKeyPair(algorithm, extractable, usages, &public_key, + &private_key)); + + // Key generation success using exponent with leading zeros. + exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), + public_exponent.begin(), + public_exponent.end()); + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, + exponent_with_leading_zeros); + EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, + &public_key, &private_key)); + EXPECT_FALSE(public_key.isNull()); + EXPECT_FALSE(private_key.isNull()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); + EXPECT_TRUE(public_key.extractable()); + EXPECT_EQ(extractable, private_key.extractable()); + EXPECT_EQ(public_usages, public_key.usages()); + EXPECT_EQ(private_usages, private_key.usages()); + + // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha1) + algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1, modulus_length, public_exponent); + EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, false, usages, + &public_key, &private_key)); + EXPECT_FALSE(public_key.isNull()); + EXPECT_FALSE(private_key.isNull()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); + EXPECT_EQ(modulus_length, + public_key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_EQ(modulus_length, + private_key.algorithm().rsaHashedParams()->modulusLengthBits()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + public_key.algorithm().rsaHashedParams()->hash().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + private_key.algorithm().rsaHashedParams()->hash().id()); + // Even though "extractable" was set to false, the public key remains + // extractable. + EXPECT_TRUE(public_key.extractable()); + EXPECT_FALSE(private_key.extractable()); + EXPECT_EQ(public_usages, public_key.usages()); + EXPECT_EQ(private_usages, private_key.usages()); + + // Exporting a private key as SPKI format doesn't make sense. However this + // will first fail because the key is not extractable. + std::vector<uint8_t> output; + EXPECT_EQ(Status::ErrorKeyNotExtractable(), + ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); + + // Re-generate an extractable private_key and try to export it as SPKI format. + // This should fail since spki is for public keys. + EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, true, usages, + &public_key, &private_key)); + EXPECT_EQ(Status::ErrorUnexpectedKeyType(), + ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); +} + +TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadModulusLength) { + const unsigned int kBadModulusBits[] = { + 0, + 248, // Too small. + 257, // Not a multiple of 8. + 1023, // Not a multiple of 8. + 0xFFFFFFFF, // Too big. + 16384 + 8, // 16384 is the maxmimum length that NSS succeeds for. + }; + + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + + for (size_t i = 0; i < arraysize(kBadModulusBits); ++i) { + const unsigned int modulus_length_bits = kBadModulusBits[i]; + blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length_bits, + public_exponent); + bool extractable = true; + const blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), + GenerateKeyPair(algorithm, extractable, usages, &public_key, + &private_key)); + } +} + +// Try generating RSA key pairs using unsupported public exponents. Only +// exponents of 3 and 65537 are supported. Although OpenSSL can support other +// values, it can also hang when given invalid exponents. To avoid hanging, use +// a whitelist of known safe exponents. +TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadExponent) { + const unsigned int modulus_length = 1024; + + const char* const kPublicExponents[] = { + "11", // 17 - This is a valid public exponent, but currently disallowed. + "00", + "01", + "02", + "010000", // 65536 + }; + + for (size_t i = 0; i < arraysize(kPublicExponents); ++i) { + SCOPED_TRACE(i); + blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, modulus_length, + HexStringToBytes(kPublicExponents[i])); + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), + GenerateKeyPair(algorithm, true, blink::WebCryptoKeyUsageSign, + &public_key, &private_key)); + } +} + +TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) { + // Import a key pair. + blink::WebCryptoAlgorithm import_algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1); + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, + blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, &public_key, + &private_key)); + + blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); + + std::vector<uint8_t> signature; + bool signature_match; + + // Compute a signature. + const std::vector<uint8_t> data = HexStringToBytes("010203040506070809"); + ASSERT_EQ(Status::Success(), + Sign(algorithm, private_key, CryptoData(data), &signature)); + + // Ensure truncated signature does not verify by passing one less byte. + EXPECT_EQ(Status::Success(), + Verify(algorithm, public_key, + CryptoData(vector_as_array(&signature), + static_cast<unsigned int>(signature.size()) - 1), + CryptoData(data), &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure truncated signature does not verify by passing no bytes. + EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, CryptoData(), + CryptoData(data), &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure corrupted signature does not verify. + std::vector<uint8_t> corrupt_sig = signature; + corrupt_sig[corrupt_sig.size() / 2] ^= 0x1; + EXPECT_EQ(Status::Success(), + Verify(algorithm, public_key, CryptoData(corrupt_sig), + CryptoData(data), &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure signatures that are greater than the modulus size fail. + const unsigned int long_message_size_bytes = 1024; + DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8); + const unsigned char kLongSignature[long_message_size_bytes] = {0}; + EXPECT_EQ(Status::Success(), + Verify(algorithm, public_key, + CryptoData(kLongSignature, sizeof(kLongSignature)), + CryptoData(data), &signature_match)); + EXPECT_FALSE(signature_match); + + // Ensure that signing and verifying with an incompatible algorithm fails. + algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep); + + EXPECT_EQ(Status::ErrorUnexpected(), + Sign(algorithm, private_key, CryptoData(data), &signature)); + EXPECT_EQ(Status::ErrorUnexpected(), + Verify(algorithm, public_key, CryptoData(signature), + CryptoData(data), &signature_match)); + + // Some crypto libraries (NSS) can automatically select the RSA SSA inner hash + // based solely on the contents of the input signature data. In the Web Crypto + // implementation, the inner hash should be specified uniquely by the key + // algorithm parameter. To validate this behavior, call Verify with a computed + // signature that used one hash type (SHA-1), but pass in a key with a + // different inner hash type (SHA-256). If the hash type is determined by the + // signature itself (undesired), the verify will pass, while if the hash type + // is specified by the key algorithm (desired), the verify will fail. + + // Compute a signature using SHA-1 as the inner hash. + EXPECT_EQ(Status::Success(), + Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), + private_key, CryptoData(data), &signature)); + + blink::WebCryptoKey public_key_256; + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageVerify, &public_key_256)); + + // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The + // signature should not verify. + // NOTE: public_key was produced by generateKey, and so its associated + // algorithm has WebCryptoRsaKeyGenParams and not WebCryptoRsaSsaParams. Thus + // it has no inner hash to conflict with the input algorithm. + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, + private_key.algorithm().rsaHashedParams()->hash().id()); + EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, + public_key_256.algorithm().rsaHashedParams()->hash().id()); + + bool is_match; + EXPECT_EQ(Status::Success(), + Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), + public_key_256, CryptoData(signature), CryptoData(data), + &is_match)); + EXPECT_FALSE(is_match); +} + +TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests)); + + // Import the key pair. + blink::WebCryptoAlgorithm import_algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1); + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( + HexStringToBytes(kPublicKeySpkiDerHex), + HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, + blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, &public_key, + &private_key)); + + blink::WebCryptoAlgorithm algorithm = + CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); + + // Validate the signatures are computed and verified as expected. + std::vector<uint8_t> signature; + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + std::vector<uint8_t> test_message = + GetBytesFromHexString(test, "message_hex"); + std::vector<uint8_t> test_signature = + GetBytesFromHexString(test, "signature_hex"); + + signature.clear(); + ASSERT_EQ(Status::Success(), Sign(algorithm, private_key, + CryptoData(test_message), &signature)); + EXPECT_BYTES_EQ(test_signature, signature); + + bool is_match = false; + ASSERT_EQ(Status::Success(), + Verify(algorithm, public_key, CryptoData(test_signature), + CryptoData(test_message), &is_match)); + EXPECT_TRUE(is_match); + } +} + +// Try importing an RSA-SSA public key with unsupported key usages using SPKI +// format. RSA-SSA public keys only support the 'verify' usage. +TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) { + const blink::WebCryptoAlgorithm algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageEncrypt, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + }; + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + algorithm, false, bad_usages[i], &public_key)); + } +} + +// Try importing an RSA-SSA public key with unsupported key usages using JWK +// format. RSA-SSA public keys only support the 'verify' usage. +TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_JWK) { + const blink::WebCryptoAlgorithm algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256); + + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageSign, + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, + blink::WebCryptoKeyUsageEncrypt, + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, + }; + + base::DictionaryValue dict; + RestoreJwkRsaDictionary(&dict); + dict.Remove("use", NULL); + dict.SetString("alg", "RS256"); + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKeyJwkFromDict(dict, algorithm, false, bad_usages[i], + &public_key)); + } +} + +// Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports: +// 'sign', 'verify' +TEST_F(WebCryptoRsaSsaTest, GenerateKeyBadUsages) { + blink::WebCryptoKeyUsageMask bad_usages[] = { + blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDecrypt, + blink::WebCryptoKeyUsageWrapKey, + }; + + const unsigned int modulus_length = 256; + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + + for (size_t i = 0; i < arraysize(bad_usages); ++i) { + SCOPED_TRACE(i); + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, + modulus_length, public_exponent), + true, bad_usages[i], &public_key, &private_key)); + } +} + +// Generate an RSA-SSA key pair. The public and private keys should select the +// key usages which are applicable, and not have the exact same usages as was +// specified to GenerateKey +TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairIntersectUsages) { + const unsigned int modulus_length = 256; + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_EQ(Status::Success(), + GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, + modulus_length, public_exponent), + true, blink::WebCryptoKeyUsageSign | + blink::WebCryptoKeyUsageVerify, + &public_key, &private_key)); + + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, public_key.usages()); + EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); + + // Try again but this time without the Verify usages. + ASSERT_EQ(Status::Success(), + GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, + modulus_length, public_exponent), + true, blink::WebCryptoKeyUsageSign, &public_key, + &private_key)); + + EXPECT_EQ(0, public_key.usages()); + EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); +} + +TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairEmptyUsages) { + const unsigned int modulus_length = 256; + const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); + + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256, + modulus_length, public_exponent), + true, 0, &public_key, &private_key)); +} + +TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) { + blink::WebCryptoKey public_key; + blink::WebCryptoKey private_key; + + // Public without usage does not throw an error. + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, 0, &public_key)); + EXPECT_EQ(0, public_key.usages()); + + // Private empty usage will throw an error. + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, 0, &private_key)); + + std::vector<uint8_t> public_jwk; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &public_jwk)); + + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(public_jwk), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, 0, &public_key)); + EXPECT_EQ(0, public_key.usages()); + + // With correct usage to get correct imported private_key + std::vector<uint8_t> private_jwk; + ImportKey( + blink::WebCryptoKeyFormatPkcs8, + CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, blink::WebCryptoKeyUsageSign, &private_key); + + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, private_key, &private_jwk)); + + ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(private_jwk), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha1), + true, 0, &private_key)); +} + +TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) { + struct TestCase { + const blink::WebCryptoAlgorithmId hash; + const blink::WebCryptoKeyUsageMask usage; + const char* const jwk_alg; + }; + const TestCase kTests[] = { + {blink::WebCryptoAlgorithmIdSha1, blink::WebCryptoKeyUsageVerify, "RS1"}, + {blink::WebCryptoAlgorithmIdSha256, + blink::WebCryptoKeyUsageVerify, + "RS256"}, + {blink::WebCryptoAlgorithmIdSha384, + blink::WebCryptoKeyUsageVerify, + "RS384"}, + {blink::WebCryptoAlgorithmIdSha512, + blink::WebCryptoKeyUsageVerify, + "RS512"}}; + + for (size_t test_index = 0; test_index < arraysize(kTests); ++test_index) { + SCOPED_TRACE(test_index); + const TestCase& test = kTests[test_index]; + + const blink::WebCryptoAlgorithm import_algorithm = + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, test.hash); + + // Import the spki to create a public key + blink::WebCryptoKey public_key; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, + CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), + import_algorithm, true, test.usage, &public_key)); + + // Export the public key as JWK and verify its contents + std::vector<uint8_t> jwk; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); + EXPECT_TRUE(VerifyPublicJwk(jwk, test.jwk_alg, kPublicKeyModulusHex, + kPublicKeyExponentHex, test.usage)); + + // Import the JWK back in to create a new key + blink::WebCryptoKey public_key2; + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk), + import_algorithm, true, test.usage, &public_key2)); + ASSERT_TRUE(public_key2.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); + EXPECT_TRUE(public_key2.extractable()); + EXPECT_EQ(import_algorithm.id(), public_key2.algorithm().id()); + + // Export the new key as spki and compare to the original. + std::vector<uint8_t> spki; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); + EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); + } +} + +TEST_F(WebCryptoRsaSsaTest, ImportJwkRsaFailures) { + base::DictionaryValue dict; + RestoreJwkRsaDictionary(&dict); + blink::WebCryptoAlgorithm algorithm = + CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256); + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageVerify; + blink::WebCryptoKey key; + + // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) + // entry, while an RSA private key must have those plus at least a "d" + // (private exponent) entry. + // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, + // section 6.3. + + // Baseline pass. + EXPECT_EQ(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); + EXPECT_EQ(algorithm.id(), key.algorithm().id()); + EXPECT_FALSE(key.extractable()); + EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); + + // The following are specific failure cases for when kty = "RSA". + + // Fail if either "n" or "e" is not present or malformed. + const std::string kKtyParmName[] = {"n", "e"}; + for (size_t idx = 0; idx < arraysize(kKtyParmName); ++idx) { + // Fail on missing parameter. + dict.Remove(kKtyParmName[idx], NULL); + EXPECT_NE(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); + RestoreJwkRsaDictionary(&dict); + + // Fail on bad b64 parameter encoding. + dict.SetString(kKtyParmName[idx], "Qk3f0DsytU8lfza2au #$% Htaw2xpop9yTuH0"); + EXPECT_NE(Status::Success(), + ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); + RestoreJwkRsaDictionary(&dict); + + // Fail on empty parameter. + dict.SetString(kKtyParmName[idx], ""); + EXPECT_EQ(Status::ErrorJwkEmptyBigInteger(kKtyParmName[idx]), + ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); + RestoreJwkRsaDictionary(&dict); + } +} + +// Try importing an RSA-SSA key from JWK format, having specified both Sign and +// Verify usage, and an invalid JWK. +// +// The test must fail with a usage error BEFORE attempting to read the JWK data. +// Although both Sign and Verify are valid usages for RSA-SSA keys, it is +// invalid to have them both at the same time for one key (since Sign applies to +// private keys, whereas Verify applies to public keys). +// +// If the implementation does not fail fast, this test will crash dereferencing +// invalid memory. +TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaJwkBadUsageFailFast) { + CryptoData bad_data(NULL, 128); // Invalid buffer of length 128. + + blink::WebCryptoKey key; + ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), + ImportKey(blink::WebCryptoKeyFormatJwk, bad_data, + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, blink::WebCryptoKeyUsageVerify | + blink::WebCryptoKeyUsageSign, + &key)); +} + +// Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected. +TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("bad_rsa_keys.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + + const base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); + std::vector<uint8_t> key_data = + GetKeyDataFromJsonTestCase(test, key_format); + std::string test_error; + ASSERT_TRUE(test->GetString("error", &test_error)); + + blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; + if (key_format == blink::WebCryptoKeyFormatSpki) + usages = blink::WebCryptoKeyUsageVerify; + blink::WebCryptoKey key; + Status status = ImportKey(key_format, CryptoData(key_data), + CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, + blink::WebCryptoAlgorithmIdSha256), + true, usages, &key); + EXPECT_EQ(test_error, StatusToString(status)); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/secret_key_util.cc b/chromium/components/webcrypto/algorithms/secret_key_util.cc new file mode 100644 index 00000000000..dcdecfa19e6 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/secret_key_util.cc @@ -0,0 +1,91 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/webcrypto/algorithms/secret_key_util.h" + +#include <openssl/rand.h> + +#include "base/stl_util.h" +#include "components/webcrypto/algorithms/util.h" +#include "components/webcrypto/blink_key_handle.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/generate_key_result.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "crypto/openssl_util.h" + +namespace webcrypto { + +Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + unsigned int keylen_bits, + GenerateKeyResult* result) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + unsigned int keylen_bytes = NumBitsToBytes(keylen_bits); + std::vector<unsigned char> random_bytes(keylen_bytes, 0); + + if (keylen_bytes > 0) { + if (!RAND_bytes(vector_as_array(&random_bytes), keylen_bytes)) + return Status::OperationError(); + TruncateToBitLength(keylen_bits, &random_bytes); + } + + result->AssignSecretKey(blink::WebCryptoKey::create( + CreateSymmetricKeyHandle(CryptoData(random_bytes)), + blink::WebCryptoKeyTypeSecret, extractable, algorithm, usages)); + + return Status::Success(); +} + +Status CreateWebCryptoSecretKey(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + *key = blink::WebCryptoKey::create(CreateSymmetricKeyHandle(key_data), + blink::WebCryptoKeyTypeSecret, extractable, + algorithm, usages); + return Status::Success(); +} + +Status CheckSecretKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages) { + return CheckKeyCreationUsages(all_possible_usages, actual_usages, + EmptyUsagePolicy::REJECT_EMPTY); +} + +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + std::vector<uint8_t>* jwk_key_data) { + JwkWriter writer(algorithm, extractable, usages, "oct"); + writer.SetBytes("k", raw_key_data); + writer.ToJson(jwk_key_data); +} + +Status ReadSecretKeyNoExpectedAlgJwk( + const CryptoData& key_data, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data, + JwkReader* jwk) { + Status status = jwk->Init(key_data, expected_extractable, expected_usages, + "oct", std::string()); + if (status.IsError()) + return status; + + std::string jwk_k_value; + status = jwk->GetBytes("k", &jwk_k_value); + if (status.IsError()) + return status; + raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); + + return Status::Success(); +} + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/secret_key_util.h b/chromium/components/webcrypto/algorithms/secret_key_util.h new file mode 100644 index 00000000000..cfef769262a --- /dev/null +++ b/chromium/components/webcrypto/algorithms/secret_key_util.h @@ -0,0 +1,73 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_SECRET_KEY_UTIL_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_SECRET_KEY_UTIL_ + +#include <string> +#include <vector> + +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +// This file contains functions shared by multiple symmetric key algorithms. + +namespace webcrypto { + +class CryptoData; +class GenerateKeyResult; +class JwkReader; +class Status; + +// Generates a random secret key of the given bit length. If the bit length is +// not a multiple of 8, then the resulting key will have ceil(keylen_bits / 8) +// bytes, and the "unused" bits will be set to zero. This function does not do +// any validation checks on the provided parameters. +Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + unsigned int keylen_bits, + GenerateKeyResult* result); + +// Creates a WebCrypto secret key given the raw data. The provided |key_data| +// will be copied into the new key. This function does not do any validation +// checks for the provided parameters. +Status CreateWebCryptoSecretKey(const CryptoData& key_data, + const blink::WebCryptoKeyAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Checks that |actual_usages| is a non-empty subset of |all_possible_usages|. +Status CheckSecretKeyCreationUsages( + blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages); + +// Writes a JWK-formatted symmetric key to |jwk_key_data|. +// * raw_key_data: The actual key data +// * algorithm: The JWK algorithm name (i.e. "alg") +// * extractable: The JWK extractability (i.e. "ext") +// * usages: The JWK usages (i.e. "key_ops") +void WriteSecretKeyJwk(const CryptoData& raw_key_data, + const std::string& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + std::vector<uint8_t>* jwk_key_data); + +// Parses a UTF-8 encoded JWK (key_data), and extracts the key material to +// |*raw_key_data|. Returns Status::Success() on success, otherwise an error. +// In order for this to succeed: +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usages must be a subset of the JWK's "key_ops" if present. +Status ReadSecretKeyNoExpectedAlgJwk( + const CryptoData& key_data, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data, + JwkReader* jwk); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_SECRET_KEY_UTIL_ diff --git a/chromium/components/webcrypto/openssl/sha_openssl.cc b/chromium/components/webcrypto/algorithms/sha.cc index 18c687a31f5..2d8b544b545 100644 --- a/chromium/components/webcrypto/openssl/sha_openssl.cc +++ b/chromium/components/webcrypto/algorithms/sha.cc @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <vector> #include <openssl/evp.h> #include <openssl/sha.h> +#include <vector> #include "base/logging.h" #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" +#include "components/webcrypto/algorithms/util.h" #include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/openssl/util_openssl.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" @@ -24,9 +23,9 @@ namespace { // part of WebCrypto, that allows chunks of data to be streamed in before // computing a SHA-* digest (as opposed to ShaImplementation, which computes // digests over complete messages) -class DigestorOpenSsl : public blink::WebCryptoDigestor { +class DigestorImpl : public blink::WebCryptoDigestor { public: - explicit DigestorOpenSsl(blink::WebCryptoAlgorithmId algorithm_id) + explicit DigestorImpl(blink::WebCryptoAlgorithmId algorithm_id) : initialized_(false), digest_context_(EVP_MD_CTX_create()), algorithm_id_(algorithm_id) {} @@ -112,7 +111,7 @@ class ShaImplementation : public AlgorithmImplementation { Status Digest(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& data, std::vector<uint8_t>* buffer) const override { - DigestorOpenSsl digestor(algorithm.id()); + DigestorImpl digestor(algorithm.id()); Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); // http://crbug.com/366427: the spec does not define any other failures for // digest, so none of the subsequent errors are spec compliant. @@ -124,13 +123,13 @@ class ShaImplementation : public AlgorithmImplementation { } // namespace -AlgorithmImplementation* CreatePlatformShaImplementation() { - return new ShaImplementation(); +scoped_ptr<AlgorithmImplementation> CreateShaImplementation() { + return make_scoped_ptr(new ShaImplementation()); } -scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( +scoped_ptr<blink::WebCryptoDigestor> CreateDigestorImplementation( blink::WebCryptoAlgorithmId algorithm) { - return scoped_ptr<blink::WebCryptoDigestor>(new DigestorOpenSsl(algorithm)); + return scoped_ptr<blink::WebCryptoDigestor>(new DigestorImpl(algorithm)); } } // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/sha_unittest.cc b/chromium/components/webcrypto/algorithms/sha_unittest.cc new file mode 100644 index 00000000000..4e82d98763c --- /dev/null +++ b/chromium/components/webcrypto/algorithms/sha_unittest.cc @@ -0,0 +1,84 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +class WebCryptoShaTest : public WebCryptoTestBase {}; + +TEST_F(WebCryptoShaTest, DigestSampleSets) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("sha.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoAlgorithm test_algorithm = + GetDigestAlgorithm(test, "algorithm"); + std::vector<uint8_t> test_input = GetBytesFromHexString(test, "input"); + std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output"); + + std::vector<uint8_t> output; + ASSERT_EQ(Status::Success(), + Digest(test_algorithm, CryptoData(test_input), &output)); + EXPECT_BYTES_EQ(test_output, output); + } +} + +TEST_F(WebCryptoShaTest, DigestSampleSetsInChunks) { + scoped_ptr<base::ListValue> tests; + ASSERT_TRUE(ReadJsonTestFileToList("sha.json", &tests)); + + for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { + SCOPED_TRACE(test_index); + base::DictionaryValue* test; + ASSERT_TRUE(tests->GetDictionary(test_index, &test)); + + blink::WebCryptoAlgorithm test_algorithm = + GetDigestAlgorithm(test, "algorithm"); + std::vector<uint8_t> test_input = GetBytesFromHexString(test, "input"); + std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output"); + + // Test the chunk version of the digest functions. Test with 129 byte chunks + // because the SHA-512 chunk size is 128 bytes. + unsigned char* output; + unsigned int output_length; + static const size_t kChunkSizeBytes = 129; + size_t length = test_input.size(); + scoped_ptr<blink::WebCryptoDigestor> digestor( + CreateDigestor(test_algorithm.id())); + std::vector<uint8_t>::iterator begin = test_input.begin(); + size_t chunk_index = 0; + while (begin != test_input.end()) { + size_t chunk_length = std::min(kChunkSizeBytes, length - chunk_index); + std::vector<uint8_t> chunk(begin, begin + chunk_length); + ASSERT_TRUE(chunk.size() > 0); + EXPECT_TRUE(digestor->consume(vector_as_array(&chunk), + static_cast<unsigned int>(chunk.size()))); + chunk_index = chunk_index + chunk_length; + begin = begin + chunk_length; + } + EXPECT_TRUE(digestor->finish(output, output_length)); + EXPECT_BYTES_EQ(test_output, CryptoData(output, output_length)); + } +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/test_helpers.cc b/chromium/components/webcrypto/algorithms/test_helpers.cc new file mode 100644 index 00000000000..22db00fdd2b --- /dev/null +++ b/chromium/components/webcrypto/algorithms/test_helpers.cc @@ -0,0 +1,684 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/webcrypto/algorithms/test_helpers.h" + +#include <algorithm> + +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "components/test_runner/test_common.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/generate_key_result.h" +#include "components/webcrypto/jwk.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" +#include "third_party/re2/re2/re2.h" + +namespace webcrypto { + +// static +void WebCryptoTestBase::SetUpTestCase() { + test_runner::EnsureBlinkInitialized(); +} + +void PrintTo(const Status& status, ::std::ostream* os) { + *os << StatusToString(status); +} + +bool operator==(const Status& a, const Status& b) { + if (a.IsSuccess() != b.IsSuccess()) + return false; + if (a.IsSuccess()) + return true; + return a.error_type() == b.error_type() && + a.error_details() == b.error_details(); +} + +bool operator!=(const Status& a, const Status& b) { + return !(a == b); +} + +void PrintTo(const CryptoData& data, ::std::ostream* os) { + *os << "[" << base::HexEncode(data.bytes(), data.byte_length()) << "]"; +} + +bool operator==(const CryptoData& a, const CryptoData& b) { + return a.byte_length() == b.byte_length() && + memcmp(a.bytes(), b.bytes(), a.byte_length()) == 0; +} + +bool operator!=(const CryptoData& a, const CryptoData& b) { + return !(a == b); +} + +static std::string ErrorTypeToString(blink::WebCryptoErrorType type) { + switch (type) { + case blink::WebCryptoErrorTypeNotSupported: + return "NotSupported"; + case blink::WebCryptoErrorTypeType: + return "TypeError"; + case blink::WebCryptoErrorTypeData: + return "DataError"; + case blink::WebCryptoErrorTypeSyntax: + return "SyntaxError"; + case blink::WebCryptoErrorTypeOperation: + return "OperationError"; + case blink::WebCryptoErrorTypeInvalidAccess: + return "InvalidAccess"; + default: + return "?"; + } +} + +std::string StatusToString(const Status& status) { + if (status.IsSuccess()) + return "Success"; + + std::string result = ErrorTypeToString(status.error_type()); + if (!status.error_details().empty()) + result += ": " + status.error_details(); + return result; +} + +blink::WebCryptoAlgorithm CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmId algorithm_id, + const blink::WebCryptoAlgorithmId hash_id, + unsigned int modulus_length, + const std::vector<uint8_t>& public_exponent) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + algorithm_id, new blink::WebCryptoRsaHashedKeyGenParams( + CreateAlgorithm(hash_id), modulus_length, + vector_as_array(&public_exponent), + static_cast<unsigned int>(public_exponent.size()))); +} + +std::vector<uint8_t> Corrupted(const std::vector<uint8_t>& input) { + std::vector<uint8_t> corrupted_data(input); + if (corrupted_data.empty()) + corrupted_data.push_back(0); + corrupted_data[corrupted_data.size() / 2] ^= 0x01; + return corrupted_data; +} + +std::vector<uint8_t> HexStringToBytes(const std::string& hex) { + std::vector<uint8_t> bytes; + base::HexStringToBytes(hex, &bytes); + return bytes; +} + +std::vector<uint8_t> MakeJsonVector(const std::string& json_string) { + return std::vector<uint8_t>(json_string.begin(), json_string.end()); +} + +std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict) { + std::string json; + base::JSONWriter::Write(dict, &json); + return MakeJsonVector(json); +} + +::testing::AssertionResult ReadJsonTestFile(const char* test_file_name, + scoped_ptr<base::Value>* value) { + base::FilePath test_data_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir)) + return ::testing::AssertionFailure() << "Couldn't retrieve test dir"; + + base::FilePath file_path = test_data_dir.AppendASCII("components") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("webcrypto") + .AppendASCII(test_file_name); + + std::string file_contents; + if (!base::ReadFileToString(file_path, &file_contents)) { + return ::testing::AssertionFailure() + << "Couldn't read test file: " << file_path.value(); + } + + // Strip C++ style comments out of the "json" file, otherwise it cannot be + // parsed. + re2::RE2::GlobalReplace(&file_contents, re2::RE2("\\s*//.*"), ""); + + // Parse the JSON to a dictionary. + *value = base::JSONReader::Read(file_contents); + if (!value->get()) { + return ::testing::AssertionFailure() + << "Couldn't parse test file JSON: " << file_path.value(); + } + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult ReadJsonTestFileToList( + const char* test_file_name, + scoped_ptr<base::ListValue>* list) { + // Read the JSON. + scoped_ptr<base::Value> json; + ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json); + if (!result) + return result; + + // Cast to an ListValue. + base::ListValue* list_value = NULL; + if (!json->GetAsList(&list_value) || !list_value) + return ::testing::AssertionFailure() << "The JSON was not a list"; + + list->reset(list_value); + ignore_result(json.release()); + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult ReadJsonTestFileToDictionary( + const char* test_file_name, + scoped_ptr<base::DictionaryValue>* dict) { + // Read the JSON. + scoped_ptr<base::Value> json; + ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json); + if (!result) + return result; + + // Cast to an DictionaryValue. + base::DictionaryValue* dict_value = NULL; + if (!json->GetAsDictionary(&dict_value) || !dict_value) + return ::testing::AssertionFailure() << "The JSON was not a dictionary"; + + dict->reset(dict_value); + ignore_result(json.release()); + + return ::testing::AssertionSuccess(); +} + +std::vector<uint8_t> GetBytesFromHexString(const base::DictionaryValue* dict, + const std::string& property_name) { + std::string hex_string; + if (!dict->GetString(property_name, &hex_string)) { + ADD_FAILURE() << "Couldn't get string property: " << property_name; + return std::vector<uint8_t>(); + } + + return HexStringToBytes(hex_string); +} + +blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::DictionaryValue* dict, + const char* property_name) { + std::string algorithm_name; + if (!dict->GetString(property_name, &algorithm_name)) { + ADD_FAILURE() << "Couldn't get string property: " << property_name; + return blink::WebCryptoAlgorithm::createNull(); + } + + struct { + const char* name; + blink::WebCryptoAlgorithmId id; + } kDigestNameToId[] = { + {"sha-1", blink::WebCryptoAlgorithmIdSha1}, + {"sha-256", blink::WebCryptoAlgorithmIdSha256}, + {"sha-384", blink::WebCryptoAlgorithmIdSha384}, + {"sha-512", blink::WebCryptoAlgorithmIdSha512}, + }; + + for (size_t i = 0; i < arraysize(kDigestNameToId); ++i) { + if (kDigestNameToId[i].name == algorithm_name) + return CreateAlgorithm(kDigestNameToId[i].id); + } + + return blink::WebCryptoAlgorithm::createNull(); +} + +// Creates a comparator for |bufs| which operates on indices rather than values. +class CompareUsingIndex { + public: + explicit CompareUsingIndex(const std::vector<std::vector<uint8_t>>* bufs) + : bufs_(bufs) {} + + bool operator()(size_t i1, size_t i2) { return (*bufs_)[i1] < (*bufs_)[i2]; } + + private: + const std::vector<std::vector<uint8_t>>* bufs_; +}; + +bool CopiesExist(const std::vector<std::vector<uint8_t>>& bufs) { + // Sort the indices of |bufs| into a separate vector. This reduces the amount + // of data copied versus sorting |bufs| directly. + std::vector<size_t> sorted_indices(bufs.size()); + for (size_t i = 0; i < sorted_indices.size(); ++i) + sorted_indices[i] = i; + std::sort(sorted_indices.begin(), sorted_indices.end(), + CompareUsingIndex(&bufs)); + + // Scan for adjacent duplicates. + for (size_t i = 1; i < sorted_indices.size(); ++i) { + if (bufs[sorted_indices[i]] == bufs[sorted_indices[i - 1]]) + return true; + } + return false; +} + +blink::WebCryptoAlgorithm CreateAesKeyGenAlgorithm( + blink::WebCryptoAlgorithmId aes_alg_id, + unsigned short length) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + aes_alg_id, new blink::WebCryptoAesKeyGenParams(length)); +} + +// The following key pair is comprised of the SPKI (public key) and PKCS#8 +// (private key) representations of the key pair provided in Example 1 of the +// NIST test vectors at +// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt +const unsigned int kModulusLengthBits = 1024; +const char* const kPublicKeySpkiDerHex = + "30819f300d06092a864886f70d010101050003818d0030818902818100a5" + "6e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad9" + "91d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfc" + "e0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e" + "6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cf" + "fb2249bd9a21370203010001"; +const char* const kPrivateKeyPkcs8DerHex = + "30820275020100300d06092a864886f70d01010105000482025f3082025b" + "02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52" + "a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab" + "7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921c" + "b23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef" + "22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d" + "4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c" + "568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee" + "896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31" + "b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b3" + "25024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8629" + "6b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b" + "3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde12" + "3fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc72" + "3e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca" + "5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8d" + "d3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa71" + "2049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd" + "48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027" + "156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319" + "584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24" + "a79f4d"; +// The modulus and exponent (in hex) of kPublicKeySpkiDerHex +const char* const kPublicKeyModulusHex = + "A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056" + "FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B" + "8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E138" + "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; +const char* const kPublicKeyExponentHex = "010001"; + +blink::WebCryptoKey ImportSecretKeyFromRaw( + const std::vector<uint8_t>& key_raw, + const blink::WebCryptoAlgorithm& algorithm, + blink::WebCryptoKeyUsageMask usage) { + blink::WebCryptoKey key; + bool extractable = true; + EXPECT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatRaw, CryptoData(key_raw), + algorithm, extractable, usage, &key)); + + EXPECT_FALSE(key.isNull()); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(algorithm.id(), key.algorithm().id()); + EXPECT_EQ(extractable, key.extractable()); + EXPECT_EQ(usage, key.usages()); + return key; +} + +void ImportRsaKeyPair(const std::vector<uint8_t>& spki_der, + const std::vector<uint8_t>& pkcs8_der, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_key_usages, + blink::WebCryptoKeyUsageMask private_key_usages, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) { + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(spki_der), + algorithm, true, public_key_usages, public_key)); + EXPECT_FALSE(public_key->isNull()); + EXPECT_TRUE(public_key->handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key->type()); + EXPECT_EQ(algorithm.id(), public_key->algorithm().id()); + EXPECT_TRUE(public_key->extractable()); + EXPECT_EQ(public_key_usages, public_key->usages()); + + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(pkcs8_der), + algorithm, extractable, private_key_usages, private_key)); + EXPECT_FALSE(private_key->isNull()); + EXPECT_TRUE(private_key->handle()); + EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type()); + EXPECT_EQ(algorithm.id(), private_key->algorithm().id()); + EXPECT_EQ(extractable, private_key->extractable()); + EXPECT_EQ(private_key_usages, private_key->usages()); +} + +Status ImportKeyJwkFromDict(const base::DictionaryValue& dict, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + return ImportKey(blink::WebCryptoKeyFormatJwk, + CryptoData(MakeJsonVector(dict)), algorithm, extractable, + usages, key); +} + +scoped_ptr<base::DictionaryValue> GetJwkDictionary( + const std::vector<uint8_t>& json) { + base::StringPiece json_string( + reinterpret_cast<const char*>(vector_as_array(&json)), json.size()); + scoped_ptr<base::Value> value = base::JSONReader::Read(json_string); + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); + + return scoped_ptr<base::DictionaryValue>( + static_cast<base::DictionaryValue*>(value.release())); +} + +// Verifies the input dictionary contains the expected values. Exact matches are +// required on the fields examined. +::testing::AssertionResult VerifyJwk( + const scoped_ptr<base::DictionaryValue>& dict, + const std::string& kty_expected, + const std::string& alg_expected, + blink::WebCryptoKeyUsageMask use_mask_expected) { + // ---- kty + std::string value_string; + if (!dict->GetString("kty", &value_string)) + return ::testing::AssertionFailure() << "Missing 'kty'"; + if (value_string != kty_expected) + return ::testing::AssertionFailure() << "Expected 'kty' to be " + << kty_expected << "but found " + << value_string; + + // ---- alg + if (!dict->GetString("alg", &value_string)) + return ::testing::AssertionFailure() << "Missing 'alg'"; + if (value_string != alg_expected) + return ::testing::AssertionFailure() << "Expected 'alg' to be " + << alg_expected << " but found " + << value_string; + + // ---- ext + // always expect ext == true in this case + bool ext_value; + if (!dict->GetBoolean("ext", &ext_value)) + return ::testing::AssertionFailure() << "Missing 'ext'"; + if (!ext_value) + return ::testing::AssertionFailure() + << "Expected 'ext' to be true but found false"; + + // ---- key_ops + base::ListValue* key_ops; + if (!dict->GetList("key_ops", &key_ops)) + return ::testing::AssertionFailure() << "Missing 'key_ops'"; + blink::WebCryptoKeyUsageMask key_ops_mask = 0; + Status status = + GetWebCryptoUsagesFromJwkKeyOpsForTest(key_ops, &key_ops_mask); + if (status.IsError()) + return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; + if (key_ops_mask != use_mask_expected) + return ::testing::AssertionFailure() + << "Expected 'key_ops' mask to be " << use_mask_expected + << " but found " << key_ops_mask << " (" << value_string << ")"; + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult VerifySecretJwk( + const std::vector<uint8_t>& json, + const std::string& alg_expected, + const std::string& k_expected_hex, + blink::WebCryptoKeyUsageMask use_mask_expected) { + scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); + if (!dict.get() || dict->empty()) + return ::testing::AssertionFailure() << "JSON parsing failed"; + + // ---- k + std::string value_string; + if (!dict->GetString("k", &value_string)) + return ::testing::AssertionFailure() << "Missing 'k'"; + std::string k_value; + if (!Base64DecodeUrlSafe(value_string, &k_value)) + return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; + if (!base::LowerCaseEqualsASCII( + base::HexEncode(k_value.data(), k_value.size()), + k_expected_hex.c_str())) { + return ::testing::AssertionFailure() << "Expected 'k' to be " + << k_expected_hex + << " but found something different"; + } + + return VerifyJwk(dict, "oct", alg_expected, use_mask_expected); +} + +::testing::AssertionResult VerifyPublicJwk( + const std::vector<uint8_t>& json, + const std::string& alg_expected, + const std::string& n_expected_hex, + const std::string& e_expected_hex, + blink::WebCryptoKeyUsageMask use_mask_expected) { + scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); + if (!dict.get() || dict->empty()) + return ::testing::AssertionFailure() << "JSON parsing failed"; + + // ---- n + std::string value_string; + if (!dict->GetString("n", &value_string)) + return ::testing::AssertionFailure() << "Missing 'n'"; + std::string n_value; + if (!Base64DecodeUrlSafe(value_string, &n_value)) + return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(n) failed"; + if (base::HexEncode(n_value.data(), n_value.size()) != n_expected_hex) { + return ::testing::AssertionFailure() << "'n' does not match the expected " + "value"; + } + // TODO(padolph): LowerCaseEqualsASCII() does not work for above! + + // ---- e + if (!dict->GetString("e", &value_string)) + return ::testing::AssertionFailure() << "Missing 'e'"; + std::string e_value; + if (!Base64DecodeUrlSafe(value_string, &e_value)) + return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(e) failed"; + if (!base::LowerCaseEqualsASCII( + base::HexEncode(e_value.data(), e_value.size()), + e_expected_hex.c_str())) { + return ::testing::AssertionFailure() << "Expected 'e' to be " + << e_expected_hex + << " but found something different"; + } + + return VerifyJwk(dict, "RSA", alg_expected, use_mask_expected); +} + +void ImportExportJwkSymmetricKey( + int key_len_bits, + const blink::WebCryptoAlgorithm& import_algorithm, + blink::WebCryptoKeyUsageMask usages, + const std::string& jwk_alg) { + std::vector<uint8_t> json; + std::string key_hex; + + // Hardcoded pseudo-random bytes to use for keys of different lengths. + switch (key_len_bits) { + case 128: + key_hex = "3f1e7cd4f6f8543f6b1e16002e688623"; + break; + case 256: + key_hex = + "bd08286b81a74783fd1ccf46b7e05af84ee25ae021210074159e0c4d9d907692"; + break; + case 384: + key_hex = + "a22c5441c8b185602283d64c7221de1d0951e706bfc09539435ec0e0ed614e1d40" + "6623f2b31d31819fec30993380dd82"; + break; + case 512: + key_hex = + "5834f639000d4cf82de124fbfd26fb88d463e99f839a76ba41ac88967c80a3f61e" + "1239a452e573dba0750e988152988576efd75b8d0229b7aca2ada2afd392ee"; + break; + default: + FAIL() << "Unexpected key_len_bits" << key_len_bits; + } + + // Import a raw key. + blink::WebCryptoKey key = ImportSecretKeyFromRaw(HexStringToBytes(key_hex), + import_algorithm, usages); + + // Export the key in JWK format and validate. + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); + EXPECT_TRUE(VerifySecretJwk(json, jwk_alg, key_hex, usages)); + + // Import the JWK-formatted key. + ASSERT_EQ(Status::Success(), + ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(json), + import_algorithm, true, usages, &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + EXPECT_EQ(import_algorithm.id(), key.algorithm().id()); + EXPECT_EQ(true, key.extractable()); + EXPECT_EQ(usages, key.usages()); + + // Export the key in raw format and compare to the original. + std::vector<uint8_t> key_raw_out; + ASSERT_EQ(Status::Success(), + ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); + EXPECT_BYTES_EQ_HEX(key_hex, key_raw_out); +} + +Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key) { + GenerateKeyResult result; + Status status = GenerateKey(algorithm, extractable, usages, &result); + if (status.IsError()) + return status; + + if (result.type() != GenerateKeyResult::TYPE_SECRET_KEY) + return Status::ErrorUnexpected(); + + *key = result.secret_key(); + + return Status::Success(); +} + +Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key) { + GenerateKeyResult result; + Status status = GenerateKey(algorithm, extractable, usages, &result); + if (status.IsError()) + return status; + + if (result.type() != GenerateKeyResult::TYPE_PUBLIC_PRIVATE_KEY_PAIR) + return Status::ErrorUnexpected(); + + *public_key = result.public_key(); + *private_key = result.private_key(); + + return Status::Success(); +} + +blink::WebCryptoKeyFormat GetKeyFormatFromJsonTestCase( + const base::DictionaryValue* test) { + std::string format; + EXPECT_TRUE(test->GetString("key_format", &format)); + if (format == "jwk") + return blink::WebCryptoKeyFormatJwk; + else if (format == "pkcs8") + return blink::WebCryptoKeyFormatPkcs8; + else if (format == "spki") + return blink::WebCryptoKeyFormatSpki; + else if (format == "raw") + return blink::WebCryptoKeyFormatRaw; + + ADD_FAILURE() << "Unrecognized key format: " << format; + return blink::WebCryptoKeyFormatRaw; +} + +std::vector<uint8_t> GetKeyDataFromJsonTestCase( + const base::DictionaryValue* test, + blink::WebCryptoKeyFormat key_format) { + if (key_format == blink::WebCryptoKeyFormatJwk) { + const base::DictionaryValue* json; + EXPECT_TRUE(test->GetDictionary("key", &json)); + return MakeJsonVector(*json); + } + return GetBytesFromHexString(test, "key"); +} + +blink::WebCryptoNamedCurve GetCurveNameFromDictionary( + const base::DictionaryValue* dict) { + std::string curve_str; + if (!dict->GetString("crv", &curve_str)) { + ADD_FAILURE() << "Missing crv parameter"; + return blink::WebCryptoNamedCurveP384; + } + + if (curve_str == "P-256") + return blink::WebCryptoNamedCurveP256; + if (curve_str == "P-384") + return blink::WebCryptoNamedCurveP384; + if (curve_str == "P-521") + return blink::WebCryptoNamedCurveP521; + else + ADD_FAILURE() << "Unrecognized curve name: " << curve_str; + + return blink::WebCryptoNamedCurveP384; +} + +blink::WebCryptoAlgorithm CreateHmacImportAlgorithm( + blink::WebCryptoAlgorithmId hash_id, + unsigned int length_bits) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdHmac, + new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id), true, + length_bits)); +} + +blink::WebCryptoAlgorithm CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmId hash_id) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + blink::WebCryptoAlgorithmIdHmac, + new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id), false, 0)); +} + +blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, nullptr); +} + +blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmId id, + blink::WebCryptoAlgorithmId hash_id) { + DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id))); +} + +blink::WebCryptoAlgorithm CreateEcImportAlgorithm( + blink::WebCryptoAlgorithmId id, + blink::WebCryptoNamedCurve named_curve) { + return blink::WebCryptoAlgorithm::adoptParamsAndCreate( + id, new blink::WebCryptoEcKeyImportParams(named_curve)); +} + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/test_helpers.h b/chromium/components/webcrypto/algorithms/test_helpers.h new file mode 100644 index 00000000000..3cc87d22e19 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/test_helpers.h @@ -0,0 +1,236 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_TEST_HELPERS_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_TEST_HELPERS_H_ + +#include <ostream> +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +#define EXPECT_BYTES_EQ(expected, actual) \ + EXPECT_EQ(CryptoData(expected), CryptoData(actual)) + +#define EXPECT_BYTES_EQ_HEX(expected_hex, actual_bytes) \ + EXPECT_BYTES_EQ(HexStringToBytes(expected_hex), actual_bytes) + +namespace base { +class DictionaryValue; +class ListValue; +class Value; +} + +namespace blink { +class WebCryptoAlgorithm; +} + +namespace webcrypto { + +// Base class for WebCrypto tests. All WebCrypto tests must derive from this +// to ensure that Blink has been properly initialized. In particular, +// the WebCrypto tests use blink::WebCryptoAlgorithm, which in turn relies on +// PartitionAlloc. +class WebCryptoTestBase : public testing::Test { + protected: + static void SetUpTestCase(); +}; + +class Status; +class CryptoData; + +// These functions are used by GTEST to support EXPECT_EQ() for +// webcrypto::Status and webcrypto::CryptoData + +void PrintTo(const Status& status, ::std::ostream* os); +bool operator==(const Status& a, const Status& b); +bool operator!=(const Status& a, const Status& b); + +void PrintTo(const CryptoData& data, ::std::ostream* os); +bool operator==(const CryptoData& a, const CryptoData& b); +bool operator!=(const CryptoData& a, const CryptoData& b); + +// Gives a human-readable description of |status| and any error it represents. +std::string StatusToString(const Status& status); + +blink::WebCryptoAlgorithm CreateRsaHashedKeyGenAlgorithm( + blink::WebCryptoAlgorithmId algorithm_id, + const blink::WebCryptoAlgorithmId hash_id, + unsigned int modulus_length, + const std::vector<uint8_t>& public_exponent); + +// Returns a slightly modified version of the input vector. +// +// - For non-empty inputs a single bit is inverted. +// - For empty inputs, a byte is added. +std::vector<uint8_t> Corrupted(const std::vector<uint8_t>& input); + +std::vector<uint8_t> HexStringToBytes(const std::string& hex); + +std::vector<uint8_t> MakeJsonVector(const std::string& json_string); +std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict); + +// ---------------------------------------------------------------- +// Helpers for working with JSON data files for test expectations. +// ---------------------------------------------------------------- + +// Reads a file in "src/content/test/data/webcrypto" to a base::Value. +// The file must be JSON, however it can also include C++ style comments. +::testing::AssertionResult ReadJsonTestFile(const char* test_file_name, + scoped_ptr<base::Value>* value); +// Same as ReadJsonTestFile(), but returns the value as a List. +::testing::AssertionResult ReadJsonTestFileToList( + const char* test_file_name, + scoped_ptr<base::ListValue>* list); +// Same as ReadJsonTestFile(), but returns the value as a Dictionary. +::testing::AssertionResult ReadJsonTestFileToDictionary( + const char* test_file_name, + scoped_ptr<base::DictionaryValue>* dict); + +// Reads a string property from the dictionary with path |property_name| +// (which can include periods for nested dictionaries). Interprets the +// string as a hex encoded string and converts it to a bytes list. +// +// Returns empty vector on failure. +std::vector<uint8_t> GetBytesFromHexString(const base::DictionaryValue* dict, + const std::string& property_name); + +// Reads a string property with path "property_name" and converts it to a +// WebCryptoAlgorith. Returns null algorithm on failure. +blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::DictionaryValue* dict, + const char* property_name); + +// Returns true if any of the vectors in the input list have identical content. +bool CopiesExist(const std::vector<std::vector<uint8_t>>& bufs); + +blink::WebCryptoAlgorithm CreateAesKeyGenAlgorithm( + blink::WebCryptoAlgorithmId aes_alg_id, + unsigned short length); + +// The following key pair is comprised of the SPKI (public key) and PKCS#8 +// (private key) representations of the key pair provided in Example 1 of the +// NIST test vectors at +// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt +extern const unsigned int kModulusLengthBits; +extern const char* const kPublicKeySpkiDerHex; +extern const char* const kPrivateKeyPkcs8DerHex; + +// The modulus and exponent (in hex) of kPublicKeySpkiDerHex +extern const char* const kPublicKeyModulusHex; +extern const char* const kPublicKeyExponentHex; + +blink::WebCryptoKey ImportSecretKeyFromRaw( + const std::vector<uint8_t>& key_raw, + const blink::WebCryptoAlgorithm& algorithm, + blink::WebCryptoKeyUsageMask usage); + +void ImportRsaKeyPair(const std::vector<uint8_t>& spki_der, + const std::vector<uint8_t>& pkcs8_der, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask public_key_usages, + blink::WebCryptoKeyUsageMask private_key_usages, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key); + +Status ImportKeyJwkFromDict(const base::DictionaryValue& dict, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); + +// Parses a vector of JSON into a dictionary. +scoped_ptr<base::DictionaryValue> GetJwkDictionary( + const std::vector<uint8_t>& json); + +// Verifies the input dictionary contains the expected values. Exact matches are +// required on the fields examined. +::testing::AssertionResult VerifyJwk( + const scoped_ptr<base::DictionaryValue>& dict, + const std::string& kty_expected, + const std::string& alg_expected, + blink::WebCryptoKeyUsageMask use_mask_expected); + +::testing::AssertionResult VerifySecretJwk( + const std::vector<uint8_t>& json, + const std::string& alg_expected, + const std::string& k_expected_hex, + blink::WebCryptoKeyUsageMask use_mask_expected); + +// Verifies that the JSON in the input vector contains the provided +// expected values. Exact matches are required on the fields examined. +::testing::AssertionResult VerifyPublicJwk( + const std::vector<uint8_t>& json, + const std::string& alg_expected, + const std::string& n_expected_hex, + const std::string& e_expected_hex, + blink::WebCryptoKeyUsageMask use_mask_expected); + +// Helper that tests importing ane exporting of symmetric keys as JWK. +void ImportExportJwkSymmetricKey( + int key_len_bits, + const blink::WebCryptoAlgorithm& import_algorithm, + blink::WebCryptoKeyUsageMask usages, + const std::string& jwk_alg); + +// Wrappers around GenerateKey() which expect the result to be either a secret +// key or a public/private keypair. If the result does not match the +// expectation, then it fails with Status::ErrorUnexpected(). +Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* key); +Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoKey* public_key, + blink::WebCryptoKey* private_key); + +// Reads a key format string as used in some JSON test files and converts it to +// a WebCryptoKeyFormat. +blink::WebCryptoKeyFormat GetKeyFormatFromJsonTestCase( + const base::DictionaryValue* test); + +// Extracts the key data bytes from |test| as used insome JSON test files. +std::vector<uint8_t> GetKeyDataFromJsonTestCase( + const base::DictionaryValue* test, + blink::WebCryptoKeyFormat key_format); + +// Reads the "crv" string from a JSON test case and returns it as a +// WebCryptoNamedCurve. +blink::WebCryptoNamedCurve GetCurveNameFromDictionary( + const base::DictionaryValue* dict); + +// Creates an HMAC import algorithm whose inner hash algorithm is determined by +// the specified algorithm ID. It is an error to call this method with a hash +// algorithm that is not SHA*. +blink::WebCryptoAlgorithm CreateHmacImportAlgorithm( + blink::WebCryptoAlgorithmId hash_id, + unsigned int length_bits); + +// Same as above but without specifying a length. +blink::WebCryptoAlgorithm CreateHmacImportAlgorithmNoLength( + blink::WebCryptoAlgorithmId hash_id); + +// Creates a WebCryptoAlgorithm without any parameters. +blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id); + +// Creates an import algorithm for RSA algorithms that take a hash. +// It is an error to call this with a hash_id that is not a SHA*. +blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( + blink::WebCryptoAlgorithmId id, + blink::WebCryptoAlgorithmId hash_id); + +// Creates an import algorithm for EC keys. +blink::WebCryptoAlgorithm CreateEcImportAlgorithm( + blink::WebCryptoAlgorithmId id, + blink::WebCryptoNamedCurve named_curve); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_TEST_HELPERS_H_ diff --git a/chromium/components/webcrypto/algorithms/util.cc b/chromium/components/webcrypto/algorithms/util.cc new file mode 100644 index 00000000000..43e728cd61d --- /dev/null +++ b/chromium/components/webcrypto/algorithms/util.cc @@ -0,0 +1,127 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/webcrypto/algorithms/util.h" + +#include <openssl/aead.h> +#include <openssl/bn.h> +#include <openssl/digest.h> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" + +namespace webcrypto { + +const EVP_MD* GetDigest(const blink::WebCryptoAlgorithm& hash_algorithm) { + return GetDigest(hash_algorithm.id()); +} + +const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { + switch (id) { + case blink::WebCryptoAlgorithmIdSha1: + return EVP_sha1(); + case blink::WebCryptoAlgorithmIdSha256: + return EVP_sha256(); + case blink::WebCryptoAlgorithmIdSha384: + return EVP_sha384(); + case blink::WebCryptoAlgorithmIdSha512: + return EVP_sha512(); + default: + return NULL; + } +} + +void TruncateToBitLength(size_t length_bits, std::vector<uint8_t>* bytes) { + size_t length_bytes = NumBitsToBytes(length_bits); + + if (bytes->size() != length_bytes) { + CHECK_LT(length_bytes, bytes->size()); + bytes->resize(length_bytes); + } + + size_t remainder_bits = length_bits % 8; + + // Zero any "unused bits" in the final byte. + if (remainder_bits) + (*bytes)[bytes->size() - 1] &= ~((0xFF) >> remainder_bits); +} + +Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages, + EmptyUsagePolicy empty_usage_policy) { + if (actual_usages == 0 && + empty_usage_policy == EmptyUsagePolicy::REJECT_EMPTY) { + return Status::ErrorCreateKeyEmptyUsages(); + } + + if (!ContainsKeyUsages(all_possible_usages, actual_usages)) + return Status::ErrorCreateKeyBadUsages(); + return Status::Success(); +} + +bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, + blink::WebCryptoKeyUsageMask b) { + return (a & b) == b; +} + +BIGNUM* CreateBIGNUM(const std::string& n) { + return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); +} + +Status AeadEncryptDecrypt(EncryptOrDecrypt mode, + const std::vector<uint8_t>& raw_key, + const CryptoData& data, + unsigned int tag_length_bytes, + const CryptoData& iv, + const CryptoData& additional_data, + const EVP_AEAD* aead_alg, + std::vector<uint8_t>* buffer) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + EVP_AEAD_CTX ctx; + + if (!aead_alg) + return Status::ErrorUnexpected(); + + if (!EVP_AEAD_CTX_init(&ctx, aead_alg, vector_as_array(&raw_key), + raw_key.size(), tag_length_bytes, NULL)) { + return Status::OperationError(); + } + + crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup> ctx_cleanup(&ctx); + + size_t len; + int ok; + + if (mode == DECRYPT) { + if (data.byte_length() < tag_length_bytes) + return Status::ErrorDataTooSmall(); + + buffer->resize(data.byte_length() - tag_length_bytes); + + ok = EVP_AEAD_CTX_open(&ctx, vector_as_array(buffer), &len, buffer->size(), + iv.bytes(), iv.byte_length(), data.bytes(), + data.byte_length(), additional_data.bytes(), + additional_data.byte_length()); + } else { + // No need to check for unsigned integer overflow here (seal fails if + // the output buffer is too small). + buffer->resize(data.byte_length() + EVP_AEAD_max_overhead(aead_alg)); + + ok = EVP_AEAD_CTX_seal(&ctx, vector_as_array(buffer), &len, buffer->size(), + iv.bytes(), iv.byte_length(), data.bytes(), + data.byte_length(), additional_data.bytes(), + additional_data.byte_length()); + } + + if (!ok) + return Status::OperationError(); + buffer->resize(len); + return Status::Success(); +} + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/algorithms/util.h b/chromium/components/webcrypto/algorithms/util.h new file mode 100644 index 00000000000..8fa292651f0 --- /dev/null +++ b/chromium/components/webcrypto/algorithms/util.h @@ -0,0 +1,96 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_UTIL_H_ +#define COMPONENTS_WEBCRYPTO_ALGORITHMS_UTIL_H_ + +#include <string> +#include <vector> + +#include <openssl/base.h> + +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +// This file contains miscellaneous helpers that don't belong in any of the +// other *_util.h + +namespace webcrypto { + +class CryptoData; +class GenerateKeyResult; +class Status; + +// Returns the EVP_MD that corresponds with |hash_algorithm|, or nullptr on +// failure. +const EVP_MD* GetDigest(const blink::WebCryptoAlgorithm& hash_algorithm); + +// Returns the EVP_MD that corresponds with |id|, or nullptr on failure. +const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id); + +// Truncates an octet string to a particular bit length. This is accomplished by +// resizing to the closest byte length, and then zero-ing the unused +// least-significant bits of the final byte. +// +// It is an error to call this function with a bit length that is larger than +// that of |bytes|. +// +// TODO(eroman): This operation is not yet defined by the WebCrypto spec, +// however this is a reasonable interpretation: +// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27402 +void TruncateToBitLength(size_t length_bits, std::vector<uint8_t>* bytes); + +// Rounds a bit count (up) to the nearest byte count. +// +// This is mathematically equivalent to (x + 7) / 8, however has no +// possibility of integer overflow. +template <typename T> +T NumBitsToBytes(T x) { + return (x / 8) + (7 + (x % 8)) / 8; +} + +enum class EmptyUsagePolicy { + // Allow keys to have empty usages. + ALLOW_EMPTY, + + // Do not allow keys to have empty usages. + REJECT_EMPTY, +}; + +// Verifies whether a key can be created using |actual_usages| when the +// algorithm supports |all_possible_usages|. +Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, + blink::WebCryptoKeyUsageMask actual_usages, + EmptyUsagePolicy empty_usage_policy); + +// TODO(eroman): This doesn't really belong in this file. Move it into Blink +// instead. +// +// Returns true if the set bits in b make up a subset of the set bits in a. +bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, + blink::WebCryptoKeyUsageMask b); + +// Allocates a new BIGNUM given a std::string big-endian representation. +BIGNUM* CreateBIGNUM(const std::string& n); + +// The values of these constants correspond with the "enc" parameter of +// EVP_CipherInit_ex(), do not change. +enum EncryptOrDecrypt { DECRYPT = 0, ENCRYPT = 1 }; + +// Does either encryption or decryption for an AEAD algorithm. +// * |mode| controls whether encryption or decryption is done +// * |aead_alg| the algorithm (for instance AES-GCM) +// * |buffer| where the ciphertext or plaintext is written to. +Status AeadEncryptDecrypt(EncryptOrDecrypt mode, + const std::vector<uint8_t>& raw_key, + const CryptoData& data, + unsigned int tag_length_bytes, + const CryptoData& iv, + const CryptoData& additional_data, + const EVP_AEAD* aead_alg, + std::vector<uint8_t>* buffer); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_ALGORITHMS_UTIL_H_ diff --git a/chromium/components/webcrypto/blink_key_handle.cc b/chromium/components/webcrypto/blink_key_handle.cc new file mode 100644 index 00000000000..6b8c1f4cdba --- /dev/null +++ b/chromium/components/webcrypto/blink_key_handle.cc @@ -0,0 +1,111 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/webcrypto/blink_key_handle.h" + +#include "base/logging.h" +#include "base/macros.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" + +namespace webcrypto { + +namespace { + +class SymKey; +class AsymKey; + +// Base class for wrapping OpenSSL keys in a type that can be passed to +// Blink (blink::WebCryptoKeyHandle). +// +// In addition to the key's internal OpenSSL representation (EVP_PKEY or just +// raw bytes), each key maintains a copy of its serialized form in either +// 'raw', 'pkcs8', or 'spki' format. This is to allow structured cloning of +// keys to be done synchronously from the target Blink thread, without having to +// lock access to the key throughout the code. +class Key : public blink::WebCryptoKeyHandle { + public: + explicit Key(const CryptoData& serialized_key_data) + : serialized_key_data_( + serialized_key_data.bytes(), + serialized_key_data.bytes() + serialized_key_data.byte_length()) {} + + ~Key() override {} + + // Helpers to add some safety to casting. + virtual SymKey* AsSymKey() { return nullptr; } + virtual AsymKey* AsAsymKey() { return nullptr; } + + const std::vector<uint8_t>& serialized_key_data() const { + return serialized_key_data_; + } + + private: + const std::vector<uint8_t> serialized_key_data_; +}; + +class SymKey : public Key { + public: + explicit SymKey(const CryptoData& raw_key_data) : Key(raw_key_data) {} + + SymKey* AsSymKey() override { return this; } + + const std::vector<uint8_t>& raw_key_data() const { + return serialized_key_data(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SymKey); +}; + +class AsymKey : public Key { + public: + AsymKey(crypto::ScopedEVP_PKEY pkey, + const std::vector<uint8_t>& serialized_key_data) + : Key(CryptoData(serialized_key_data)), pkey_(pkey.Pass()) {} + + AsymKey* AsAsymKey() override { return this; } + + EVP_PKEY* pkey() { return pkey_.get(); } + + private: + crypto::ScopedEVP_PKEY pkey_; + + DISALLOW_COPY_AND_ASSIGN(AsymKey); +}; + +Key* GetKey(const blink::WebCryptoKey& key) { + return reinterpret_cast<Key*>(key.handle()); +} + +} // namespace + +const std::vector<uint8_t>& GetSymmetricKeyData( + const blink::WebCryptoKey& key) { + DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + return GetKey(key)->AsSymKey()->raw_key_data(); +} + +EVP_PKEY* GetEVP_PKEY(const blink::WebCryptoKey& key) { + DCHECK_NE(blink::WebCryptoKeyTypeSecret, key.type()); + return GetKey(key)->AsAsymKey()->pkey(); +} + +const std::vector<uint8_t>& GetSerializedKeyData( + const blink::WebCryptoKey& key) { + return GetKey(key)->serialized_key_data(); +} + +blink::WebCryptoKeyHandle* CreateSymmetricKeyHandle( + const CryptoData& key_bytes) { + return new SymKey(key_bytes); +} + +blink::WebCryptoKeyHandle* CreateAsymmetricKeyHandle( + crypto::ScopedEVP_PKEY pkey, + const std::vector<uint8_t>& serialized_key_data) { + return new AsymKey(pkey.Pass(), serialized_key_data); +} + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/blink_key_handle.h b/chromium/components/webcrypto/blink_key_handle.h new file mode 100644 index 00000000000..049657c6195 --- /dev/null +++ b/chromium/components/webcrypto/blink_key_handle.h @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEBCRYPTO_BLINK_KEY_HANDLE_H_ +#define COMPONENTS_WEBCRYPTO_BLINK_KEY_HANDLE_H_ + +#include <openssl/base.h> +#include <stdint.h> + +#include <vector> + +#include "crypto/scoped_openssl_types.h" +#include "third_party/WebKit/public/platform/WebCryptoKey.h" + +// Blink keys (blink::WebCryptoKey) have an associated key handle +// (blink::WebCryptoKeyHandle) used to store custom data. This is where the +// underlying EVP_PKEY is stored for asymmetric keys, or an std::vector +// containing the bytes for symmetric keys. +// +// This file contains helpers for creating the key handles, and extracting +// properties from it. + +namespace webcrypto { + +class CryptoData; + +// Returns a reference to the symmetric key data wrapped by the given Blink +// key. The returned reference is owned by |key|. This function must only be +// called on secret keys (HMAC, AES, etc). +const std::vector<uint8_t>& GetSymmetricKeyData(const blink::WebCryptoKey& key); + +// Returns the EVP_PKEY* wrapped by the given Blink key. The returned pointer +// is owned by |key|. This function must only be called on asymmetric keys +// (RSA, EC, etc). +EVP_PKEY* GetEVP_PKEY(const blink::WebCryptoKey& key); + +// Returns a reference to the serialized key data. This reference is owned by +// |key|. This function can be called for any key type. +const std::vector<uint8_t>& GetSerializedKeyData( + const blink::WebCryptoKey& key); + +// Creates a symmetric key handle that can be passed to Blink. The caller takes +// ownership of the returned pointer. +blink::WebCryptoKeyHandle* CreateSymmetricKeyHandle( + const CryptoData& key_bytes); + +// Creates an asymmetric key handle that can be passed to Blink. The caller +// takes +// ownership of the returned pointer. +// +// TODO(eroman): This should _move_ input serialized_key_data rather than +// create a copy, since all the callers are passing in vectors that are later +// thrown away anyway. +blink::WebCryptoKeyHandle* CreateAsymmetricKeyHandle( + crypto::ScopedEVP_PKEY pkey, + const std::vector<uint8_t>& serialized_key_data); + +} // namespace webcrypto + +#endif // COMPONENTS_WEBCRYPTO_BLINK_KEY_HANDLE_H_ diff --git a/chromium/components/webcrypto/jwk.cc b/chromium/components/webcrypto/jwk.cc index 98a8b1b85f4..023ea826bee 100644 --- a/chromium/components/webcrypto/jwk.cc +++ b/chromium/components/webcrypto/jwk.cc @@ -12,14 +12,9 @@ #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" +#include "components/webcrypto/algorithms/util.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" - -// TODO(eroman): The algorithm-specific logic in this file for AES and RSA -// should be moved into the corresponding AlgorithmImplementation file. It -// exists in this file to avoid duplication between OpenSSL and NSS -// implementations. // JSON Web Key Format (JWK) is defined by: // http://tools.ietf.org/html/draft-ietf-jose-json-web-key @@ -373,213 +368,6 @@ void JwkWriter::ToJson(std::vector<uint8_t>* utf8_bytes) const { utf8_bytes->assign(json.begin(), json.end()); } -Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data, - JwkReader* jwk) { - Status status = jwk->Init(key_data, expected_extractable, expected_usages, - "oct", std::string()); - if (status.IsError()) - return status; - - std::string jwk_k_value; - status = jwk->GetBytes("k", &jwk_k_value); - if (status.IsError()) - return status; - raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); - - return Status::Success(); -} - -void WriteSecretKeyJwk(const CryptoData& raw_key_data, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data) { - JwkWriter writer(algorithm, extractable, usages, "oct"); - writer.SetBytes("k", raw_key_data); - writer.ToJson(jwk_key_data); -} - -Status ReadSecretKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data) { - JwkReader jwk; - Status status = ReadSecretKeyNoExpectedAlg( - key_data, expected_extractable, expected_usages, raw_key_data, &jwk); - if (status.IsError()) - return status; - return jwk.VerifyAlg(expected_alg); -} - -std::string MakeJwkAesAlgorithmName(const std::string& suffix, - size_t keylen_bytes) { - if (keylen_bytes == 16) - return std::string("A128") + suffix; - if (keylen_bytes == 24) - return std::string("A192") + suffix; - if (keylen_bytes == 32) - return std::string("A256") + suffix; - return std::string(); -} - -Status ReadAesSecretKeyJwk(const CryptoData& key_data, - const std::string& algorithm_name_suffix, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data) { - JwkReader jwk; - Status status = ReadSecretKeyNoExpectedAlg( - key_data, expected_extractable, expected_usages, raw_key_data, &jwk); - if (status.IsError()) - return status; - - bool has_jwk_alg; - std::string jwk_alg; - status = jwk.GetAlg(&jwk_alg, &has_jwk_alg); - if (status.IsError()) - return status; - - if (has_jwk_alg) { - std::string expected_algorithm_name = - MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); - - if (jwk_alg != expected_algorithm_name) { - // Give a different error message if the key length was wrong. - if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || - jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || - jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { - return Status::ErrorJwkIncorrectKeyLength(); - } - return Status::ErrorJwkAlgorithmInconsistent(); - } - } - - return Status::Success(); -} - -// Writes an RSA public key to a JWK dictionary -void WriteRsaPublicKeyJwk(const CryptoData& n, - const CryptoData& e, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data) { - JwkWriter writer(algorithm, extractable, usages, "RSA"); - writer.SetBytes("n", n); - writer.SetBytes("e", e); - writer.ToJson(jwk_key_data); -} - -// Writes an RSA private key to a JWK dictionary -void WriteRsaPrivateKeyJwk(const CryptoData& n, - const CryptoData& e, - const CryptoData& d, - const CryptoData& p, - const CryptoData& q, - const CryptoData& dp, - const CryptoData& dq, - const CryptoData& qi, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data) { - JwkWriter writer(algorithm, extractable, usages, "RSA"); - - writer.SetBytes("n", n); - writer.SetBytes("e", e); - writer.SetBytes("d", d); - // Although these are "optional" in the JWA, WebCrypto spec requires them to - // be emitted. - writer.SetBytes("p", p); - writer.SetBytes("q", q); - writer.SetBytes("dp", dp); - writer.SetBytes("dq", dq); - writer.SetBytes("qi", qi); - writer.ToJson(jwk_key_data); -} - -JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { -} - -JwkRsaInfo::~JwkRsaInfo() { -} - -Status ReadRsaKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - JwkRsaInfo* result) { - JwkReader jwk; - Status status = jwk.Init(key_data, expected_extractable, expected_usages, - "RSA", expected_alg); - if (status.IsError()) - return status; - - // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry - // in the JWK, while an RSA private key must have those, plus at least a "d" - // (private exponent) entry. - // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, - // section 6.3. - status = jwk.GetBigInteger("n", &result->n); - if (status.IsError()) - return status; - status = jwk.GetBigInteger("e", &result->e); - if (status.IsError()) - return status; - - result->is_private_key = jwk.HasMember("d"); - if (!result->is_private_key) - return Status::Success(); - - status = jwk.GetBigInteger("d", &result->d); - if (status.IsError()) - return status; - - // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA - // spec. However they are required by Chromium's WebCrypto implementation. - - status = jwk.GetBigInteger("p", &result->p); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("q", &result->q); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("dp", &result->dp); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("dq", &result->dq); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("qi", &result->qi); - if (status.IsError()) - return status; - - return Status::Success(); -} - -const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { - switch (hash) { - case blink::WebCryptoAlgorithmIdSha1: - return "HS1"; - case blink::WebCryptoAlgorithmIdSha256: - return "HS256"; - case blink::WebCryptoAlgorithmIdSha384: - return "HS384"; - case blink::WebCryptoAlgorithmIdSha512: - return "HS512"; - default: - return NULL; - } -} - bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { // The JSON web signature spec specifically says that padding is omitted. if (input.find_first_of("+/=") != std::string::npos) diff --git a/chromium/components/webcrypto/jwk.h b/chromium/components/webcrypto/jwk.h index 5ab62feef1d..411a986e106 100644 --- a/chromium/components/webcrypto/jwk.h +++ b/chromium/components/webcrypto/jwk.h @@ -126,106 +126,6 @@ class JwkWriter { base::DictionaryValue dict_; }; -// Writes a JWK-formatted symmetric key to |jwk_key_data|. -// * raw_key_data: The actual key data -// * algorithm: The JWK algorithm name (i.e. "alg") -// * extractable: The JWK extractability (i.e. "ext") -// * usages: The JWK usages (i.e. "key_ops") -void WriteSecretKeyJwk(const CryptoData& raw_key_data, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data); - -// Parses a UTF-8 encoded JWK (key_data), and extracts the key material to -// |*raw_key_data|. Returns Status::Success() on success, otherwise an error. -// In order for this to succeed: -// * expected_alg must match the JWK's "alg", if present. -// * expected_extractable must be consistent with the JWK's "ext", if -// present. -// * expected_usages must be a subset of the JWK's "key_ops" if present. -Status ReadSecretKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data); - -// Creates an AES algorithm name for the given key size (in bytes). For -// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. -std::string MakeJwkAesAlgorithmName(const std::string& suffix, - size_t keylen_bytes); - -// This is very similar to ReadSecretKeyJwk(), except instead of specifying an -// absolute "expected_alg", the suffix for an AES algorithm name is given -// (See MakeJwkAesAlgorithmName() for an explanation of what the suffix is). -// -// This is because the algorithm name for AES keys is dependent on the length -// of the key. This function expects key lengths to be either 128, 192, or 256 -// bits. -Status ReadAesSecretKeyJwk(const CryptoData& key_data, - const std::string& algorithm_name_suffix, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data); - -// Writes a JWK-formated RSA public key and saves the result to -// |*jwk_key_data|. -void WriteRsaPublicKeyJwk(const CryptoData& n, - const CryptoData& e, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data); - -// Writes a JWK-formated RSA private key and saves the result to -// |*jwk_key_data|. -void WriteRsaPrivateKeyJwk(const CryptoData& n, - const CryptoData& e, - const CryptoData& d, - const CryptoData& p, - const CryptoData& q, - const CryptoData& dp, - const CryptoData& dq, - const CryptoData& qi, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data); - -// Describes the RSA components for a parsed key. The names of the properties -// correspond with those from the JWK spec. Note that Chromium's WebCrypto -// implementation does not support multi-primes, so there is no parsed field -// for othinfo. -struct JwkRsaInfo { - JwkRsaInfo(); - ~JwkRsaInfo(); - - bool is_private_key; - std::string n; - std::string e; - std::string d; - std::string p; - std::string q; - std::string dp; - std::string dq; - std::string qi; -}; - -// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to -// |*result|. Returns Status::Success() on success, otherwise an error. -// In order for this to succeed: -// * expected_alg must match the JWK's "alg", if present. -// * expected_extractable must be consistent with the JWK's "ext", if -// present. -// * expected_usages must be a subset of the JWK's "key_ops" if present. -Status ReadRsaKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - JwkRsaInfo* result); - -const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash); - // This decodes JWK's flavor of base64 encoding, as described by: // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36#section-2 // diff --git a/chromium/components/webcrypto/nss/aes_algorithm_nss.cc b/chromium/components/webcrypto/nss/aes_algorithm_nss.cc deleted file mode 100644 index d09f5eecbec..00000000000 --- a/chromium/components/webcrypto/nss/aes_algorithm_nss.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/nss/aes_algorithm_nss.h" - -#include "base/logging.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/jwk.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/sym_key_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -AesAlgorithm::AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, - blink::WebCryptoKeyUsageMask all_key_usages, - const std::string& jwk_suffix) - : import_mechanism_(import_mechanism), - all_key_usages_(all_key_usages), - jwk_suffix_(jwk_suffix) { -} - -AesAlgorithm::AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, - const std::string& jwk_suffix) - : import_mechanism_(import_mechanism), - all_key_usages_(blink::WebCryptoKeyUsageEncrypt | - blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageWrapKey | - blink::WebCryptoKeyUsageUnwrapKey), - jwk_suffix_(jwk_suffix) { -} - -Status AesAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const { - Status status = CheckKeyCreationUsages(all_key_usages_, usages, false); - if (status.IsError()) - return status; - - unsigned int keylen_bits; - status = GetAesKeyGenLengthInBits(algorithm.aesKeyGenParams(), &keylen_bits); - if (status.IsError()) - return status; - - return GenerateSecretKeyNss( - blink::WebCryptoKeyAlgorithm::createAes(algorithm.id(), keylen_bits), - extractable, usages, keylen_bits, CKM_AES_KEY_GEN, result); -} - -Status AesAlgorithm::VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const { - switch (format) { - case blink::WebCryptoKeyFormatRaw: - case blink::WebCryptoKeyFormatJwk: - return CheckKeyCreationUsages(all_key_usages_, usages, false); - default: - return Status::ErrorUnsupportedImportKeyFormat(); - } -} -Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const { - const unsigned int keylen_bytes = key_data.byte_length(); - Status status = VerifyAesKeyLengthForImport(keylen_bytes); - if (status.IsError()) - return status; - - // No possibility of overflow. - unsigned int keylen_bits = keylen_bytes * 8; - - return ImportKeyRawNss(key_data, blink::WebCryptoKeyAlgorithm::createAes( - algorithm.id(), keylen_bits), - extractable, usages, import_mechanism_, key); -} - -Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const { - std::vector<uint8_t> raw_data; - Status status = ReadAesSecretKeyJwk(key_data, jwk_suffix_, extractable, - usages, &raw_data); - if (status.IsError()) - return status; - - return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, - key); -} - -Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const { - *buffer = SymKeyNss::Cast(key)->raw_key_data(); - return Status::Success(); -} - -Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const { - SymKeyNss* sym_key = SymKeyNss::Cast(key); - const std::vector<uint8_t>& raw_data = sym_key->raw_key_data(); - - WriteSecretKeyJwk(CryptoData(raw_data), - MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()), - key.extractable(), key.usages(), buffer); - - return Status::Success(); -} - -Status AesAlgorithm::SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const { - key_data->assign(SymKeyNss::Cast(key)->serialized_key_data()); - return Status::Success(); -} - -Status AesAlgorithm::DeserializeKeyForClone( - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) const { - return ImportKeyRaw(key_data, CreateAlgorithm(algorithm.id()), extractable, - usages, key); -} - -Status AesAlgorithm::GetKeyLength( - const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits) const { - return GetAesKeyLength(key_length_algorithm, has_length_bits, length_bits); -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/aes_algorithm_nss.h b/chromium/components/webcrypto/nss/aes_algorithm_nss.h deleted file mode 100644 index 1d1df1a3a84..00000000000 --- a/chromium/components/webcrypto/nss/aes_algorithm_nss.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_NSS_AES_ALGORITHM_NSS_H_ -#define COMPONENTS_WEBCRYPTO_NSS_AES_ALGORITHM_NSS_H_ - -#include <pkcs11t.h> - -#include "components/webcrypto/algorithm_implementation.h" - -namespace webcrypto { - -// Base class for AES algorithms that provides the implementation for key -// creation and export. -class AesAlgorithm : public AlgorithmImplementation { - public: - // Constructs an AES algorithm whose keys will be imported using the NSS - // mechanism |import_mechanism|. - // |all_key_usages| is the set of all WebCrypto key usages that are - // allowed for imported or generated keys. |jwk_suffix| is the suffix - // used when constructing JWK names for the algorithm. For instance A128CBC - // is the JWK name for 128-bit AES-CBC. The |jwk_suffix| in this case would - // be "CBC". - AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, - blink::WebCryptoKeyUsageMask all_key_usages, - const std::string& jwk_suffix); - - // This is the same as the other AesAlgorithm constructor, however - // |all_key_usages| is pre-filled with values for encryption/decryption - // algorithms (supports usages for: encrypt, decrypt, wrap, unwrap). - AesAlgorithm(CK_MECHANISM_TYPE import_mechanism, - const std::string& jwk_suffix); - - Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const override; - - Status VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const override; - - Status ImportKeyRaw(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override; - - Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override; - - Status ExportKeyRaw(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override; - - Status ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override; - - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override; - - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) const override; - - Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits) const override; - - private: - const CK_MECHANISM_TYPE import_mechanism_; - const blink::WebCryptoKeyUsageMask all_key_usages_; - const std::string jwk_suffix_; -}; - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_NSS_AES_ALGORITHM_NSS_H_ diff --git a/chromium/components/webcrypto/nss/aes_cbc_nss.cc b/chromium/components/webcrypto/nss/aes_cbc_nss.cc deleted file mode 100644 index 16748469f1e..00000000000 --- a/chromium/components/webcrypto/nss/aes_cbc_nss.cc +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <cryptohi.h> - -#include "base/numerics/safe_math.h" -#include "base/stl_util.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/aes_algorithm_nss.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" - -namespace webcrypto { - -namespace { - -Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, - const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) { - const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); - if (!params) - return Status::ErrorUnexpected(); - - CryptoData iv(params->iv().data(), params->iv().size()); - if (iv.byte_length() != 16) - return Status::ErrorIncorrectSizeAesCbcIv(); - - PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); - - CK_ATTRIBUTE_TYPE operation = (mode == ENCRYPT) ? CKA_ENCRYPT : CKA_DECRYPT; - - SECItem iv_item = MakeSECItemForBuffer(iv); - - crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); - if (!param) - return Status::OperationError(); - - crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( - CKM_AES_CBC_PAD, operation, sym_key, param.get())); - - if (!context.get()) - return Status::OperationError(); - - // Oddly PK11_CipherOp takes input and output lengths as "int" rather than - // "unsigned int". Do some checks now to avoid integer overflowing. - base::CheckedNumeric<int> output_max_len = data.byte_length(); - output_max_len += AES_BLOCK_SIZE; - if (!output_max_len.IsValid()) { - // TODO(eroman): Handle this by chunking the input fed into NSS. Right now - // it doesn't make much difference since the one-shot API would end up - // blowing out the memory and crashing anyway. - return Status::ErrorDataTooLarge(); - } - - // PK11_CipherOp does an invalid memory access when given empty decryption - // input, or input which is not a multiple of the block size. See also - // https://bugzilla.mozilla.com/show_bug.cgi?id=921687. - if (operation == CKA_DECRYPT && - (data.byte_length() == 0 || (data.byte_length() % AES_BLOCK_SIZE != 0))) { - return Status::OperationError(); - } - - // TODO(eroman): Refine the output buffer size. It can be computed exactly for - // encryption, and can be smaller for decryption. - buffer->resize(output_max_len.ValueOrDie()); - - unsigned char* buffer_data = vector_as_array(buffer); - - int output_len; - if (SECSuccess != PK11_CipherOp(context.get(), buffer_data, &output_len, - buffer->size(), data.bytes(), - data.byte_length())) { - return Status::OperationError(); - } - - unsigned int final_output_chunk_len; - if (SECSuccess != - PK11_DigestFinal(context.get(), buffer_data + output_len, - &final_output_chunk_len, - (output_max_len - output_len).ValueOrDie())) { - return Status::OperationError(); - } - - buffer->resize(final_output_chunk_len + output_len); - return Status::Success(); -} - -class AesCbcImplementation : public AesAlgorithm { - public: - AesCbcImplementation() : AesAlgorithm(CKM_AES_CBC, "CBC") {} - - Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); - } - - Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformAesCbcImplementation() { - return new AesCbcImplementation; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/aes_gcm_nss.cc b/chromium/components/webcrypto/nss/aes_gcm_nss.cc deleted file mode 100644 index e4b8f752e1d..00000000000 --- a/chromium/components/webcrypto/nss/aes_gcm_nss.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/numerics/safe_math.h" -#include "base/stl_util.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/aes_algorithm_nss.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" - -// At the time of this writing: -// * Windows and Mac builds ship with their own copy of NSS (3.15+) -// * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+ -// on other distros). -// -// Since NSS provides AES-GCM support starting in version 3.15, it may be -// unavailable for Linux Chrome users. -// -// * !defined(CKM_AES_GCM) -// -// This means that at build time, the NSS header pkcs11t.h is older than -// 3.15. However at runtime support may be present. -// -// TODO(eroman): Simplify this once 3.15+ is required by Linux builds. -#if !defined(CKM_AES_GCM) -#define CKM_AES_GCM 0x00001087 - -struct CK_GCM_PARAMS { - CK_BYTE_PTR pIv; - CK_ULONG ulIvLen; - CK_BYTE_PTR pAAD; - CK_ULONG ulAADLen; - CK_ULONG ulTagBits; -}; -#endif // !defined(CKM_AES_GCM) - -namespace webcrypto { - -namespace { - -Status NssSupportsAesGcm() { - if (NssRuntimeSupport::Get()->IsAesGcmSupported()) - return Status::Success(); - return Status::ErrorUnsupported( - "NSS version doesn't support AES-GCM. Try using version 3.15 or later"); -} - -// Helper to either encrypt or decrypt for AES-GCM. The result of encryption is -// the concatenation of the ciphertext and the authentication tag. Similarly, -// this is the expectation for the input to decryption. -Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, - const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) { - Status status = NssSupportsAesGcm(); - if (status.IsError()) - return status; - - PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); - const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); - if (!params) - return Status::ErrorUnexpected(); - - unsigned int tag_length_bits; - status = GetAesGcmTagLengthInBits(params, &tag_length_bits); - if (status.IsError()) - return status; - unsigned int tag_length_bytes = tag_length_bits / 8; - - CryptoData iv(params->iv()); - CryptoData additional_data(params->optionalAdditionalData()); - - CK_GCM_PARAMS gcm_params = {0}; - gcm_params.pIv = const_cast<unsigned char*>(iv.bytes()); - gcm_params.ulIvLen = iv.byte_length(); - - gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes()); - gcm_params.ulAADLen = additional_data.byte_length(); - - gcm_params.ulTagBits = tag_length_bits; - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&gcm_params); - param.len = sizeof(gcm_params); - - base::CheckedNumeric<unsigned int> buffer_size(data.byte_length()); - - // Calculate the output buffer size. - if (mode == ENCRYPT) { - buffer_size += tag_length_bytes; - if (!buffer_size.IsValid()) - return Status::ErrorDataTooLarge(); - } - - // TODO(eroman): In theory the buffer allocated for the plain text should be - // sized as |data.byte_length() - tag_length_bytes|. - // - // However NSS has a bug whereby it will fail if the output buffer size is - // not at least as large as the ciphertext: - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674 - // - // From the analysis of that bug it looks like it might be safe to pass a - // correctly sized buffer but lie about its size. Since resizing the - // WebCryptoArrayBuffer is expensive that hack may be worth looking into. - - buffer->resize(buffer_size.ValueOrDie()); - unsigned char* buffer_data = vector_as_array(buffer); - - PK11_EncryptDecryptFunction encrypt_or_decrypt_func = - (mode == ENCRYPT) ? NssRuntimeSupport::Get()->pk11_encrypt_func() - : NssRuntimeSupport::Get()->pk11_decrypt_func(); - - unsigned int output_len = 0; - SECStatus result = encrypt_or_decrypt_func( - sym_key, CKM_AES_GCM, ¶m, buffer_data, &output_len, buffer->size(), - data.bytes(), data.byte_length()); - - if (result != SECSuccess) - return Status::OperationError(); - - // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug - // above). - buffer->resize(output_len); - - return Status::Success(); -} - -class AesGcmImplementation : public AesAlgorithm { - public: - AesGcmImplementation() : AesAlgorithm(CKM_AES_GCM, "GCM") {} - - Status VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const override { - // Prevent importing AES-GCM keys if it is unavailable. - Status status = NssSupportsAesGcm(); - if (status.IsError()) - return status; - return AesAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usages); - } - - Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const override { - // Prevent generating AES-GCM keys if it is unavailable. - Status status = NssSupportsAesGcm(); - if (status.IsError()) - return status; - - return AesAlgorithm::GenerateKey(algorithm, extractable, usages, result); - } - - Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); - } - - Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformAesGcmImplementation() { - return new AesGcmImplementation; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/aes_kw_nss.cc b/chromium/components/webcrypto/nss/aes_kw_nss.cc deleted file mode 100644 index 77afb1fc53b..00000000000 --- a/chromium/components/webcrypto/nss/aes_kw_nss.cc +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <secerr.h> - -#include "base/numerics/safe_math.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/aes_algorithm_nss.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/sym_key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -// The Default IV for AES-KW. See http://www.ietf.org/rfc/rfc3394.txt -// Section 2.2.3.1. -const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; - -// The result of unwrapping is a SymKey rather than a buffer. This is a -// consequence of how NSS exposes AES-KW. Subsequent code can extract the value -// of the sym key to interpret it as key bytes in another format. -Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, - PK11SymKey* wrapping_key, - CK_MECHANISM_TYPE mechanism, - CK_FLAGS flags, - crypto::ScopedPK11SymKey* unwrapped_key) { - DCHECK_GE(wrapped_key_data.byte_length(), 24u); - DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); - - SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); - crypto::ScopedSECItem param_item( - PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); - if (!param_item) - return Status::ErrorUnexpected(); - - SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); - - // The plaintext length is always 64 bits less than the data size. - const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; - -#if defined(USE_NSS_CERTS) - // Part of workaround for - // https://bugzilla.mozilla.org/show_bug.cgi?id=981170. See the explanation - // later in this function. - PORT_SetError(0); -#endif - - crypto::ScopedPK11SymKey new_key(PK11_UnwrapSymKeyWithFlags( - wrapping_key, CKM_NSS_AES_KEY_WRAP, param_item.get(), &cipher_text, - mechanism, CKA_FLAGS_ONLY, plaintext_length, flags)); - - // TODO(padolph): Use NSS PORT_GetError() and friends to report a more - // accurate error, providing if doesn't leak any information to web pages - // about other web crypto users, key details, etc. - if (!new_key) - return Status::OperationError(); - -#if defined(USE_NSS_CERTS) - // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=981170 - // which was fixed in NSS 3.16.0. - // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, - // with a reasonable length but with key data pointing to uninitialized - // memory. - // To understand this workaround see the fix for 981170: - // https://hg.mozilla.org/projects/nss/rev/753bb69e543c - if (!NSS_VersionCheck("3.16") && PORT_GetError() == SEC_ERROR_BAD_DATA) - return Status::OperationError(); -#endif - - *unwrapped_key = new_key.Pass(); - return Status::Success(); -} - -Status WrapSymKeyAesKw(PK11SymKey* key, - PK11SymKey* wrapping_key, - std::vector<uint8_t>* buffer) { - // The data size must be at least 16 bytes and a multiple of 8 bytes. - // RFC 3394 does not specify a maximum allowed data length, but since only - // keys are being wrapped in this application (which are small), a reasonable - // max limit is whatever will fit into an unsigned. For the max size test, - // note that AES Key Wrap always adds 8 bytes to the input data size. - const unsigned int input_length = PK11_GetKeyLength(key); - DCHECK_GE(input_length, 16u); - DCHECK((input_length % 8) == 0); - - SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); - crypto::ScopedSECItem param_item( - PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); - if (!param_item) - return Status::ErrorUnexpected(); - - base::CheckedNumeric<unsigned int> output_length = input_length; - output_length += 8; - if (!output_length.IsValid()) - return Status::ErrorDataTooLarge(); - - buffer->resize(output_length.ValueOrDie()); - SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(*buffer)); - - if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, param_item.get(), - wrapping_key, key, &wrapped_key_item)) { - return Status::OperationError(); - } - if (output_length.ValueOrDie() != wrapped_key_item.len) - return Status::ErrorUnexpected(); - - return Status::Success(); -} - -class AesKwCryptoAlgorithmNss : public AesAlgorithm { - public: - AesKwCryptoAlgorithmNss() - : AesAlgorithm( - CKM_NSS_AES_KEY_WRAP, - blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, - "KW") {} - - Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& wrapping_key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - if (data.byte_length() < 16) - return Status::ErrorDataTooSmall(); - if (data.byte_length() % 8) - return Status::ErrorInvalidAesKwDataLength(); - - // Due to limitations in the NSS API for the AES-KW algorithm, |data| must - // be temporarily viewed as a symmetric key to be wrapped (encrypted). - SECItem data_item = MakeSECItemForBuffer(data); - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - crypto::ScopedPK11SymKey data_as_sym_key( - PK11_ImportSymKey(slot.get(), CKK_GENERIC_SECRET, PK11_OriginUnwrap, - CKA_SIGN, &data_item, NULL)); - if (!data_as_sym_key) - return Status::OperationError(); - - return WrapSymKeyAesKw(data_as_sym_key.get(), - SymKeyNss::Cast(wrapping_key)->key(), buffer); - } - - Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& wrapping_key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - if (data.byte_length() < 24) - return Status::ErrorDataTooSmall(); - if (data.byte_length() % 8) - return Status::ErrorInvalidAesKwDataLength(); - - // Due to limitations in the NSS API for the AES-KW algorithm, |data| must - // be temporarily viewed as a symmetric key to be unwrapped (decrypted). - crypto::ScopedPK11SymKey decrypted; - Status status = - DoUnwrapSymKeyAesKw(data, SymKeyNss::Cast(wrapping_key)->key(), - CKK_GENERIC_SECRET, 0, &decrypted); - if (status.IsError()) - return status; - - // Once the decrypt is complete, extract the resultant raw bytes from NSS - // and return them to the caller. - if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) - return Status::OperationError(); - const SECItem* const key_data = PK11_GetKeyData(decrypted.get()); - if (!key_data) - return Status::OperationError(); - buffer->assign(key_data->data, key_data->data + key_data->len); - - return Status::Success(); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformAesKwImplementation() { - return new AesKwCryptoAlgorithmNss; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/hmac_nss.cc b/chromium/components/webcrypto/nss/hmac_nss.cc deleted file mode 100644 index f19648d7707..00000000000 --- a/chromium/components/webcrypto/nss/hmac_nss.cc +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <cryptohi.h> -#include <pk11pub.h> -#include <secerr.h> -#include <sechash.h> - -#include "base/logging.h" -#include "base/stl_util.h" -#include "components/webcrypto/algorithm_implementation.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/jwk.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/sym_key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/secure_util.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -const blink::WebCryptoKeyUsageMask kAllKeyUsages = - blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; - -bool WebCryptoHashToHMACMechanism(const blink::WebCryptoAlgorithm& algorithm, - CK_MECHANISM_TYPE* mechanism) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - *mechanism = CKM_SHA_1_HMAC; - return true; - case blink::WebCryptoAlgorithmIdSha256: - *mechanism = CKM_SHA256_HMAC; - return true; - case blink::WebCryptoAlgorithmIdSha384: - *mechanism = CKM_SHA384_HMAC; - return true; - case blink::WebCryptoAlgorithmIdSha512: - *mechanism = CKM_SHA512_HMAC; - return true; - default: - return false; - } -} - -class HmacImplementation : public AlgorithmImplementation { - public: - HmacImplementation() {} - - Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const override { - Status status = CheckKeyCreationUsages(kAllKeyUsages, usages, false); - if (status.IsError()) - return status; - - const blink::WebCryptoHmacKeyGenParams* params = - algorithm.hmacKeyGenParams(); - - const blink::WebCryptoAlgorithm& hash = params->hash(); - CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; - if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) - return Status::ErrorUnsupported(); - - unsigned int keylen_bits = 0; - status = GetHmacKeyGenLengthInBits(params, &keylen_bits); - if (status.IsError()) - return status; - - return GenerateSecretKeyNss( - blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), - extractable, usages, keylen_bits, mechanism, result); - } - - Status VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const override { - switch (format) { - case blink::WebCryptoKeyFormatRaw: - case blink::WebCryptoKeyFormatJwk: - return CheckKeyCreationUsages(kAllKeyUsages, usages, false); - default: - return Status::ErrorUnsupportedImportKeyFormat(); - } - } - - Status ImportKeyRaw(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override { - const blink::WebCryptoHmacImportParams* params = - algorithm.hmacImportParams(); - - CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; - if (!WebCryptoHashToHMACMechanism(params->hash(), &mechanism)) - return Status::ErrorUnsupported(); - - unsigned int keylen_bits = 0; - Status status = GetHmacImportKeyLengthBits(params, key_data.byte_length(), - &keylen_bits); - if (status.IsError()) - return status; - - const blink::WebCryptoKeyAlgorithm key_algorithm = - blink::WebCryptoKeyAlgorithm::createHmac(params->hash().id(), - keylen_bits); - - // If no bit truncation was requested, then done! - if ((keylen_bits % 8) == 0) { - return ImportKeyRawNss(key_data, key_algorithm, extractable, usages, - mechanism, key); - } - - // Otherwise zero out the unused bits in the key data before importing. - std::vector<uint8_t> modified_key_data( - key_data.bytes(), key_data.bytes() + key_data.byte_length()); - TruncateToBitLength(keylen_bits, &modified_key_data); - return ImportKeyRawNss(CryptoData(modified_key_data), key_algorithm, - extractable, usages, mechanism, key); - } - - Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override { - const char* algorithm_name = - GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); - if (!algorithm_name) - return Status::ErrorUnexpected(); - - std::vector<uint8_t> raw_data; - Status status = ReadSecretKeyJwk(key_data, algorithm_name, extractable, - usages, &raw_data); - if (status.IsError()) - return status; - - return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, - key); - } - - Status ExportKeyRaw(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override { - *buffer = SymKeyNss::Cast(key)->raw_key_data(); - return Status::Success(); - } - - Status ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override { - SymKeyNss* sym_key = SymKeyNss::Cast(key); - const std::vector<uint8_t>& raw_data = sym_key->raw_key_data(); - - const char* algorithm_name = - GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); - if (!algorithm_name) - return Status::ErrorUnexpected(); - - WriteSecretKeyJwk(CryptoData(raw_data), algorithm_name, key.extractable(), - key.usages(), buffer); - - return Status::Success(); - } - - Status Sign(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - const blink::WebCryptoAlgorithm& hash = - key.algorithm().hmacParams()->hash(); - PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); - - CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; - if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) - return Status::ErrorUnexpected(); - - SECItem param_item = {siBuffer, NULL, 0}; - SECItem data_item = MakeSECItemForBuffer(data); - // First call is to figure out the length. - SECItem signature_item = {siBuffer, NULL, 0}; - - if (PK11_SignWithSymKey(sym_key, mechanism, ¶m_item, &signature_item, - &data_item) != SECSuccess) { - return Status::OperationError(); - } - - DCHECK_NE(0u, signature_item.len); - - buffer->resize(signature_item.len); - signature_item.data = vector_as_array(buffer); - - if (PK11_SignWithSymKey(sym_key, mechanism, ¶m_item, &signature_item, - &data_item) != SECSuccess) { - return Status::OperationError(); - } - - CHECK_EQ(buffer->size(), signature_item.len); - return Status::Success(); - } - - Status Verify(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) const override { - std::vector<uint8_t> result; - Status status = Sign(algorithm, key, data, &result); - - if (status.IsError()) - return status; - - // Do not allow verification of truncated MACs. - *signature_match = - result.size() == signature.byte_length() && - crypto::SecureMemEqual(vector_as_array(&result), signature.bytes(), - signature.byte_length()); - - return Status::Success(); - } - - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override { - key_data->assign(SymKeyNss::Cast(key)->serialized_key_data()); - return Status::Success(); - } - - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) const override { - CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; - if (!WebCryptoHashToHMACMechanism(algorithm.hmacParams()->hash(), - &mechanism)) - return Status::ErrorUnsupported(); - return ImportKeyRawNss(key_data, algorithm, extractable, usages, mechanism, - key); - } - - Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits) const override { - return GetHmacKeyLength(key_length_algorithm, has_length_bits, length_bits); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformHmacImplementation() { - return new HmacImplementation; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/key_nss.cc b/chromium/components/webcrypto/nss/key_nss.cc deleted file mode 100644 index 92f26c3e1ab..00000000000 --- a/chromium/components/webcrypto/nss/key_nss.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/nss/key_nss.h" - -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" - -namespace webcrypto { - -KeyNss::KeyNss(const CryptoData& serialized_key_data) - : serialized_key_data_( - serialized_key_data.bytes(), - serialized_key_data.bytes() + serialized_key_data.byte_length()) { -} - -KeyNss::~KeyNss() { -} - -SymKeyNss* KeyNss::AsSymKey() { - return NULL; -} - -PublicKeyNss* KeyNss::AsPublicKey() { - return NULL; -} - -PrivateKeyNss* KeyNss::AsPrivateKey() { - return NULL; -} - -SymKeyNss::~SymKeyNss() { -} - -SymKeyNss* SymKeyNss::Cast(const blink::WebCryptoKey& key) { - KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); - return platform_key->AsSymKey(); -} - -SymKeyNss* SymKeyNss::AsSymKey() { - return this; -} - -SymKeyNss::SymKeyNss(crypto::ScopedPK11SymKey key, - const CryptoData& raw_key_data) - : KeyNss(raw_key_data), key_(key.Pass()) { -} - -PublicKeyNss::~PublicKeyNss() { -} - -PublicKeyNss* PublicKeyNss::Cast(const blink::WebCryptoKey& key) { - KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); - return platform_key->AsPublicKey(); -} - -PublicKeyNss* PublicKeyNss::AsPublicKey() { - return this; -} - -PublicKeyNss::PublicKeyNss(crypto::ScopedSECKEYPublicKey key, - const CryptoData& spki_data) - : KeyNss(spki_data), key_(key.Pass()) { -} - -PrivateKeyNss::~PrivateKeyNss() { -} - -PrivateKeyNss* PrivateKeyNss::Cast(const blink::WebCryptoKey& key) { - KeyNss* platform_key = reinterpret_cast<KeyNss*>(key.handle()); - return platform_key->AsPrivateKey(); -} - -PrivateKeyNss* PrivateKeyNss::AsPrivateKey() { - return this; -} - -PrivateKeyNss::PrivateKeyNss(crypto::ScopedSECKEYPrivateKey key, - const CryptoData& pkcs8_data) - : KeyNss(pkcs8_data), key_(key.Pass()) { -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/key_nss.h b/chromium/components/webcrypto/nss/key_nss.h deleted file mode 100644 index e3127d8d58f..00000000000 --- a/chromium/components/webcrypto/nss/key_nss.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_NSS_KEY_NSS_H_ -#define COMPONENTS_WEBCRYPTO_NSS_KEY_NSS_H_ - -#include <stdint.h> -#include <vector> - -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoKey.h" - -namespace webcrypto { - -class CryptoData; -class PrivateKeyNss; -class PublicKeyNss; -class SymKeyNss; - -// Base key class for all NSS keys, used to safely cast between types. Each key -// maintains a copy of its serialized form in either 'raw', 'pkcs8', or 'spki' -// format. This is to allow structured cloning of keys synchronously from the -// target Blink thread without having to lock access to the key. -class KeyNss : public blink::WebCryptoKeyHandle { - public: - explicit KeyNss(const CryptoData& serialized_key_data); - ~KeyNss() override; - - virtual SymKeyNss* AsSymKey(); - virtual PublicKeyNss* AsPublicKey(); - virtual PrivateKeyNss* AsPrivateKey(); - - const std::vector<uint8_t>& serialized_key_data() const { - return serialized_key_data_; - } - - private: - const std::vector<uint8_t> serialized_key_data_; -}; - -class SymKeyNss : public KeyNss { - public: - ~SymKeyNss() override; - SymKeyNss(crypto::ScopedPK11SymKey key, const CryptoData& raw_key_data); - - static SymKeyNss* Cast(const blink::WebCryptoKey& key); - - PK11SymKey* key() { return key_.get(); } - SymKeyNss* AsSymKey() override; - - const std::vector<uint8_t>& raw_key_data() const { - return serialized_key_data(); - } - - private: - crypto::ScopedPK11SymKey key_; - - DISALLOW_COPY_AND_ASSIGN(SymKeyNss); -}; - -class PublicKeyNss : public KeyNss { - public: - ~PublicKeyNss() override; - PublicKeyNss(crypto::ScopedSECKEYPublicKey key, const CryptoData& spki_data); - - static PublicKeyNss* Cast(const blink::WebCryptoKey& key); - - SECKEYPublicKey* key() { return key_.get(); } - PublicKeyNss* AsPublicKey() override; - - const std::vector<uint8_t>& spki_data() const { - return serialized_key_data(); - } - - private: - crypto::ScopedSECKEYPublicKey key_; - - DISALLOW_COPY_AND_ASSIGN(PublicKeyNss); -}; - -class PrivateKeyNss : public KeyNss { - public: - ~PrivateKeyNss() override; - PrivateKeyNss(crypto::ScopedSECKEYPrivateKey key, - const CryptoData& pkcs8_data); - - static PrivateKeyNss* Cast(const blink::WebCryptoKey& key); - - SECKEYPrivateKey* key() { return key_.get(); } - PrivateKeyNss* AsPrivateKey() override; - - const std::vector<uint8_t>& pkcs8_data() const { - return serialized_key_data(); - } - - private: - crypto::ScopedSECKEYPrivateKey key_; - - DISALLOW_COPY_AND_ASSIGN(PrivateKeyNss); -}; - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_NSS_KEY_NSS_H_ diff --git a/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.cc b/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.cc deleted file mode 100644 index 1b7e4a2df4e..00000000000 --- a/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.cc +++ /dev/null @@ -1,858 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/nss/rsa_hashed_algorithm_nss.h" - -#include <secasn1.h> - -#include "base/logging.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/jwk.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -#if defined(USE_NSS_CERTS) && !defined(OS_CHROMEOS) -Status ErrorRsaPrivateKeyImportNotSupported() { - return Status::ErrorUnsupported( - "NSS version must be at least 3.16.2 for RSA private key import. See " - "http://crbug.com/380424"); -} - -// Prior to NSS 3.16.2 RSA key parameters were not validated. This is -// a security problem for RSA private key import from JWK which uses a -// CKA_ID based on the public modulus to retrieve the private key. -Status NssSupportsRsaPrivateKeyImport() { - if (!NSS_VersionCheck("3.16.2")) - return ErrorRsaPrivateKeyImportNotSupported(); - - // Also ensure that the version of Softoken is 3.16.2 or later. - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - CK_SLOT_INFO info = {}; - if (PK11_GetSlotInfo(slot.get(), &info) != SECSuccess) - return ErrorRsaPrivateKeyImportNotSupported(); - - // CK_SLOT_INFO.hardwareVersion contains the major.minor - // version info for Softoken in the corresponding .major/.minor - // fields, and .firmwareVersion contains the patch.build - // version info (in the .major/.minor fields) - if ((info.hardwareVersion.major > 3) || - (info.hardwareVersion.major == 3 && - (info.hardwareVersion.minor > 16 || - (info.hardwareVersion.minor == 16 && - info.firmwareVersion.major >= 2)))) { - return Status::Success(); - } - - return ErrorRsaPrivateKeyImportNotSupported(); -} -#else -Status NssSupportsRsaPrivateKeyImport() { - return Status::Success(); -} -#endif - -bool CreateRsaHashedPublicKeyAlgorithm( - blink::WebCryptoAlgorithmId rsa_algorithm, - blink::WebCryptoAlgorithmId hash_algorithm, - SECKEYPublicKey* key, - blink::WebCryptoKeyAlgorithm* key_algorithm) { - // TODO(eroman): What about other key types rsaPss, rsaOaep. - if (!key || key->keyType != rsaKey) - return false; - - unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; - CryptoData public_exponent(key->u.rsa.publicExponent.data, - key->u.rsa.publicExponent.len); - - *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( - rsa_algorithm, modulus_length_bits, public_exponent.bytes(), - public_exponent.byte_length(), hash_algorithm); - return true; -} - -bool CreateRsaHashedPrivateKeyAlgorithm( - blink::WebCryptoAlgorithmId rsa_algorithm, - blink::WebCryptoAlgorithmId hash_algorithm, - SECKEYPrivateKey* key, - blink::WebCryptoKeyAlgorithm* key_algorithm) { - crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); - if (!public_key) - return false; - return CreateRsaHashedPublicKeyAlgorithm(rsa_algorithm, hash_algorithm, - public_key.get(), key_algorithm); -} - -// From PKCS#1 [http://tools.ietf.org/html/rfc3447]: -// -// RSAPrivateKey ::= SEQUENCE { -// version Version, -// modulus INTEGER, -- n -// publicExponent INTEGER, -- e -// privateExponent INTEGER, -- d -// prime1 INTEGER, -- p -// prime2 INTEGER, -- q -// exponent1 INTEGER, -- d mod (p-1) -// exponent2 INTEGER, -- d mod (q-1) -// coefficient INTEGER, -- (inverse of q) mod p -// otherPrimeInfos OtherPrimeInfos OPTIONAL -// } -// -// Note that otherPrimeInfos is only applicable for version=1. Since NSS -// doesn't use multi-prime can safely use version=0. -struct RSAPrivateKey { - SECItem version; - SECItem modulus; - SECItem public_exponent; - SECItem private_exponent; - SECItem prime1; - SECItem prime2; - SECItem exponent1; - SECItem exponent2; - SECItem coefficient; -}; - -// The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo -// function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we -// provide a fallback implementation. -#if defined(USE_NSS_CERTS) -const SEC_ASN1Template RSAPrivateKeyTemplate[] = { - {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, version)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, modulus)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, public_exponent)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, private_exponent)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime1)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime2)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent1)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent2)}, - {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, coefficient)}, - {0}}; -#endif // defined(USE_NSS_CERTS) - -// On success |value| will be filled with data which must be freed by -// SECITEM_FreeItem(value, PR_FALSE); -bool ReadUint(SECKEYPrivateKey* key, - CK_ATTRIBUTE_TYPE attribute, - SECItem* value) { - SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value); - - // PK11_ReadRawAttribute() returns items of type siBuffer. However in order - // for the ASN.1 encoding to be correct, the items must be of type - // siUnsignedInteger. - value->type = siUnsignedInteger; - - return rv == SECSuccess; -} - -// Fills |out| with the RSA private key properties. Returns true on success. -// Regardless of the return value, the caller must invoke FreeRSAPrivateKey() -// to free up any allocated memory. -// -// The passed in RSAPrivateKey must be zero-initialized. -bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) { - if (key->keyType != rsaKey) - return false; - - // Everything should be zero-ed out. These are just some spot checks. - DCHECK(!out->version.data); - DCHECK(!out->version.len); - DCHECK(!out->modulus.data); - DCHECK(!out->modulus.len); - - // Always use version=0 since not using multi-prime. - if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0)) - return false; - - if (!ReadUint(key, CKA_MODULUS, &out->modulus)) - return false; - if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent)) - return false; - if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent)) - return false; - if (!ReadUint(key, CKA_PRIME_1, &out->prime1)) - return false; - if (!ReadUint(key, CKA_PRIME_2, &out->prime2)) - return false; - if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1)) - return false; - if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2)) - return false; - if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient)) - return false; - - return true; -} - -struct FreeRsaPrivateKey { - void operator()(RSAPrivateKey* out) { - SECITEM_FreeItem(&out->version, PR_FALSE); - SECITEM_FreeItem(&out->modulus, PR_FALSE); - SECITEM_FreeItem(&out->public_exponent, PR_FALSE); - SECITEM_FreeItem(&out->private_exponent, PR_FALSE); - SECITEM_FreeItem(&out->prime1, PR_FALSE); - SECITEM_FreeItem(&out->prime2, PR_FALSE); - SECITEM_FreeItem(&out->exponent1, PR_FALSE); - SECITEM_FreeItem(&out->exponent2, PR_FALSE); - SECITEM_FreeItem(&out->coefficient, PR_FALSE); - } -}; - -typedef scoped_ptr<CERTSubjectPublicKeyInfo, - crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, - SECKEY_DestroySubjectPublicKeyInfo>> - ScopedCERTSubjectPublicKeyInfo; - -struct DestroyGenericObject { - void operator()(PK11GenericObject* o) const { - if (o) - PK11_DestroyGenericObject(o); - } -}; - -typedef scoped_ptr<PK11GenericObject, DestroyGenericObject> - ScopedPK11GenericObject; - -// Helper to add an attribute to a template. -void AddAttribute(CK_ATTRIBUTE_TYPE type, - void* value, - unsigned long length, - std::vector<CK_ATTRIBUTE>* templ) { - CK_ATTRIBUTE attribute = {type, value, length}; - templ->push_back(attribute); -} - -void AddAttribute(CK_ATTRIBUTE_TYPE type, - const CryptoData& data, - std::vector<CK_ATTRIBUTE>* templ) { - CK_ATTRIBUTE attribute = { - type, const_cast<unsigned char*>(data.bytes()), data.byte_length()}; - templ->push_back(attribute); -} - -void AddAttribute(CK_ATTRIBUTE_TYPE type, - const std::string& data, - std::vector<CK_ATTRIBUTE>* templ) { - AddAttribute(type, CryptoData(data), templ); -} - -Status ExportKeyPkcs8Nss(SECKEYPrivateKey* key, std::vector<uint8_t>* buffer) { - if (key->keyType != rsaKey) - return Status::ErrorUnsupported(); - -// TODO(rsleevi): Implement OAEP support according to the spec. - -#if defined(USE_NSS_CERTS) - // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code. - const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; - const int kPrivateKeyInfoVersion = 0; - - SECKEYPrivateKeyInfo private_key_info = {}; - RSAPrivateKey rsa_private_key = {}; - scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key( - &rsa_private_key); - - // http://crbug.com/366427: the spec does not define any other failures for - // exporting, so none of the subsequent errors are spec compliant. - if (!InitRSAPrivateKey(key, &rsa_private_key)) - return Status::OperationError(); - - crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); - if (!arena.get()) - return Status::OperationError(); - - if (!SEC_ASN1EncodeItem(arena.get(), &private_key_info.privateKey, - &rsa_private_key, RSAPrivateKeyTemplate)) { - return Status::OperationError(); - } - - if (SECSuccess != SECOID_SetAlgorithmID(arena.get(), - &private_key_info.algorithm, - algorithm, NULL)) { - return Status::OperationError(); - } - - if (!SEC_ASN1EncodeInteger(arena.get(), &private_key_info.version, - kPrivateKeyInfoVersion)) { - return Status::OperationError(); - } - - crypto::ScopedSECItem encoded_key( - SEC_ASN1EncodeItem(NULL, NULL, &private_key_info, - SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate))); -#else // defined(USE_NSS_CERTS) - crypto::ScopedSECItem encoded_key(PK11_ExportDERPrivateKeyInfo(key, NULL)); -#endif // defined(USE_NSS_CERTS) - - if (!encoded_key.get()) - return Status::OperationError(); - - buffer->assign(encoded_key->data, encoded_key->data + encoded_key->len); - return Status::Success(); -} - -Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const JwkRsaInfo& params, - blink::WebCryptoKey* key) { - Status status = NssSupportsRsaPrivateKeyImport(); - if (status.IsError()) - return status; - - CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - CK_BBOOL ck_false = CK_FALSE; - - std::vector<CK_ATTRIBUTE> key_template; - - AddAttribute(CKA_CLASS, &obj_class, sizeof(obj_class), &key_template); - AddAttribute(CKA_KEY_TYPE, &key_type, sizeof(key_type), &key_template); - AddAttribute(CKA_TOKEN, &ck_false, sizeof(ck_false), &key_template); - AddAttribute(CKA_SENSITIVE, &ck_false, sizeof(ck_false), &key_template); - AddAttribute(CKA_PRIVATE, &ck_false, sizeof(ck_false), &key_template); - - // Required properties by JWA. - AddAttribute(CKA_MODULUS, params.n, &key_template); - AddAttribute(CKA_PUBLIC_EXPONENT, params.e, &key_template); - AddAttribute(CKA_PRIVATE_EXPONENT, params.d, &key_template); - - // Manufacture a CKA_ID so the created key can be retrieved later as a - // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more - // direct way to do this in NSS. - // - // For consistency with other NSS key creation methods, set the CKA_ID to - // PK11_MakeIDFromPubKey(). There are some problems with - // this approach: - // - // (1) Prior to NSS 3.16.2, there is no parameter validation when creating - // private keys. It is therefore possible to construct a key using the - // known public modulus, and where all the other parameters are bogus. - // FindKeyByKeyID() returns the first key matching the ID. So this would - // effectively allow an attacker to retrieve a private key of their - // choice. - // - // (2) The ID space is shared by different key types. So theoretically - // possible to retrieve a key of the wrong type which has a matching - // CKA_ID. In practice I am told this is not likely except for small key - // sizes, since would require constructing keys with the same public - // data. - // - // (3) FindKeyByKeyID() doesn't necessarily return the object that was just - // created by CreateGenericObject. If the pre-existing key was - // provisioned with flags incompatible with WebCrypto (for instance - // marked sensitive) then this will break things. - SECItem modulus_item = MakeSECItemForBuffer(CryptoData(params.n)); - crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item)); - AddAttribute(CKA_ID, CryptoData(object_id->data, object_id->len), - &key_template); - - // Optional properties by JWA, however guaranteed to be present by Chromium's - // implementation. - AddAttribute(CKA_PRIME_1, params.p, &key_template); - AddAttribute(CKA_PRIME_2, params.q, &key_template); - AddAttribute(CKA_EXPONENT_1, params.dp, &key_template); - AddAttribute(CKA_EXPONENT_2, params.dq, &key_template); - AddAttribute(CKA_COEFFICIENT, params.qi, &key_template); - - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - - ScopedPK11GenericObject key_object(PK11_CreateGenericObject( - slot.get(), &key_template[0], key_template.size(), PR_FALSE)); - - if (!key_object) - return Status::OperationError(); - - crypto::ScopedSECKEYPrivateKey private_key_tmp( - PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL)); - - // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than - // the object created by PK11_CreateGenericObject(). - crypto::ScopedSECKEYPrivateKey private_key( - SECKEY_CopyPrivateKey(private_key_tmp.get())); - - if (!private_key) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateRsaHashedPrivateKeyAlgorithm( - algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), - private_key.get(), &key_algorithm)) { - return Status::ErrorUnexpected(); - } - - std::vector<uint8_t> pkcs8_data; - status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); - if (status.IsError()) - return status; - - scoped_ptr<PrivateKeyNss> key_handle( - new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePrivate, - extractable, key_algorithm, usages); - return Status::Success(); -} - -Status ExportKeySpkiNss(SECKEYPublicKey* key, std::vector<uint8_t>* buffer) { - const crypto::ScopedSECItem spki_der( - SECKEY_EncodeDERSubjectPublicKeyInfo(key)); - if (!spki_der) - return Status::OperationError(); - - buffer->assign(spki_der->data, spki_der->data + spki_der->len); - return Status::Success(); -} - -Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& modulus_data, - const CryptoData& exponent_data, - blink::WebCryptoKey* key) { - if (!modulus_data.byte_length()) - return Status::ErrorImportRsaEmptyModulus(); - - if (!exponent_data.byte_length()) - return Status::ErrorImportRsaEmptyExponent(); - - DCHECK(modulus_data.bytes()); - DCHECK(exponent_data.bytes()); - - // NSS does not provide a way to create an RSA public key directly from the - // modulus and exponent values, but it can import an DER-encoded ASN.1 blob - // with these values and create the public key from that. The code below - // follows the recommendation described in - // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7 - - // Pack the input values into a struct compatible with NSS ASN.1 encoding, and - // set up an ASN.1 encoder template for it. - struct RsaPublicKeyData { - SECItem modulus; - SECItem exponent; - }; - const RsaPublicKeyData pubkey_in = { - {siUnsignedInteger, - const_cast<unsigned char*>(modulus_data.bytes()), - modulus_data.byte_length()}, - {siUnsignedInteger, - const_cast<unsigned char*>(exponent_data.bytes()), - exponent_data.byte_length()}}; - const SEC_ASN1Template rsa_public_key_template[] = { - {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)}, - { - SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), - }, - { - SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), - }, - { - 0, }}; - - // DER-encode the public key. - crypto::ScopedSECItem pubkey_der( - SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template)); - if (!pubkey_der) - return Status::OperationError(); - - // Import the DER-encoded public key to create an RSA SECKEYPublicKey. - crypto::ScopedSECKEYPublicKey pubkey( - SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA)); - if (!pubkey) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateRsaHashedPublicKeyAlgorithm( - algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), - pubkey.get(), &key_algorithm)) { - return Status::ErrorUnexpected(); - } - - std::vector<uint8_t> spki_data; - Status status = ExportKeySpkiNss(pubkey.get(), &spki_data); - if (status.IsError()) - return status; - - scoped_ptr<PublicKeyNss> key_handle( - new PublicKeyNss(pubkey.Pass(), CryptoData(spki_data))); - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePublic, extractable, - key_algorithm, usages); - return Status::Success(); -} - -} // namespace - -Status RsaHashedAlgorithm::GenerateKey( - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask combined_usages, - GenerateKeyResult* result) const { - blink::WebCryptoKeyUsageMask public_usages = 0; - blink::WebCryptoKeyUsageMask private_usages = 0; - - Status status = GetUsagesForGenerateAsymmetricKey( - combined_usages, all_public_key_usages_, all_private_key_usages_, - &public_usages, &private_usages); - if (status.IsError()) - return status; - - unsigned int public_exponent = 0; - unsigned int modulus_length_bits = 0; - status = GetRsaKeyGenParameters(algorithm.rsaHashedKeyGenParams(), - &public_exponent, &modulus_length_bits); - if (status.IsError()) - return status; - - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - if (!slot) - return Status::OperationError(); - - PK11RSAGenParams rsa_gen_params; - rsa_gen_params.keySizeInBits = modulus_length_bits; - rsa_gen_params.pe = public_exponent; - - // The usages are enforced at the WebCrypto layer, so it isn't necessary to - // create keys with limited usages. - const CK_FLAGS operation_flags_mask = kAllOperationFlags; - - // The private key must be marked as insensitive and extractable, otherwise it - // cannot later be exported in unencrypted form or structured-cloned. - const PK11AttrFlags attribute_flags = - PK11_ATTR_INSENSITIVE | PK11_ATTR_EXTRACTABLE; - - // Note: NSS does not generate an sec_public_key if the call below fails, - // so there is no danger of a leaked sec_public_key. - SECKEYPublicKey* sec_public_key; - crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( - PK11_GenerateKeyPairWithOpFlags(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, - &rsa_gen_params, &sec_public_key, - attribute_flags, generate_flags_, - operation_flags_mask, NULL)); - if (!scoped_sec_private_key) - return Status::OperationError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateRsaHashedPublicKeyAlgorithm( - algorithm.id(), algorithm.rsaHashedKeyGenParams()->hash().id(), - sec_public_key, &key_algorithm)) { - return Status::ErrorUnexpected(); - } - - std::vector<uint8_t> spki_data; - status = ExportKeySpkiNss(sec_public_key, &spki_data); - if (status.IsError()) - return status; - - scoped_ptr<PublicKeyNss> public_key_handle(new PublicKeyNss( - crypto::ScopedSECKEYPublicKey(sec_public_key), CryptoData(spki_data))); - - std::vector<uint8_t> pkcs8_data; - status = ExportKeyPkcs8Nss(scoped_sec_private_key.get(), &pkcs8_data); - if (status.IsError()) - return status; - - scoped_ptr<PrivateKeyNss> private_key_handle( - new PrivateKeyNss(scoped_sec_private_key.Pass(), CryptoData(pkcs8_data))); - - blink::WebCryptoKey public_key = blink::WebCryptoKey::create( - public_key_handle.release(), blink::WebCryptoKeyTypePublic, true, - key_algorithm, public_usages); - - blink::WebCryptoKey private_key = blink::WebCryptoKey::create( - private_key_handle.release(), blink::WebCryptoKeyTypePrivate, extractable, - key_algorithm, private_usages); - - result->AssignKeyPair(public_key, private_key); - return Status::Success(); -} - -Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const { - return VerifyUsagesBeforeImportAsymmetricKey(format, all_public_key_usages_, - all_private_key_usages_, usages); -} - -Status RsaHashedAlgorithm::ImportKeyPkcs8( - const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const { - Status status = NssSupportsRsaPrivateKeyImport(); - if (status.IsError()) - return status; - - crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); - if (!arena.get()) - return Status::OperationError(); - - // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8 - // private key info object. Excess data is illegal, but NSS silently accepts - // it, so first ensure that 'key_data' consists of a single ASN.1 element. - SECItem key_item = MakeSECItemForBuffer(key_data); - SECItem pki_der; - if (SEC_QuickDERDecodeItem(arena.get(), &pki_der, - SEC_ASN1_GET(SEC_AnyTemplate), - &key_item) != SECSuccess) { - return Status::DataError(); - } - - SECKEYPrivateKey* seckey_private_key = NULL; - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), &pki_der, - NULL, // nickname - NULL, // publicValue - false, // isPerm - false, // isPrivate - KU_ALL, // usage - &seckey_private_key, - NULL) != SECSuccess) { - return Status::DataError(); - } - DCHECK(seckey_private_key); - crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key); - - const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get()); - if (sec_key_type != rsaKey) - return Status::DataError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateRsaHashedPrivateKeyAlgorithm( - algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), - private_key.get(), &key_algorithm)) { - return Status::ErrorUnexpected(); - } - - // TODO(eroman): This is probably going to be the same as the input. - std::vector<uint8_t> pkcs8_data; - status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); - if (status.IsError()) - return status; - - scoped_ptr<PrivateKeyNss> key_handle( - new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePrivate, - extractable, key_algorithm, usages); - - return Status::Success(); -} - -Status RsaHashedAlgorithm::ImportKeySpki( - const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const { - // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject - // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. - SECItem spki_item = MakeSECItemForBuffer(key_data); - const ScopedCERTSubjectPublicKeyInfo spki( - SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); - if (!spki) - return Status::DataError(); - - crypto::ScopedSECKEYPublicKey sec_public_key( - SECKEY_ExtractPublicKey(spki.get())); - if (!sec_public_key) - return Status::DataError(); - - const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); - if (sec_key_type != rsaKey) - return Status::DataError(); - - blink::WebCryptoKeyAlgorithm key_algorithm; - if (!CreateRsaHashedPublicKeyAlgorithm( - algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), - sec_public_key.get(), &key_algorithm)) { - return Status::ErrorUnexpected(); - } - - // TODO(eroman): This is probably going to be the same as the input. - std::vector<uint8_t> spki_data; - Status status = ExportKeySpkiNss(sec_public_key.get(), &spki_data); - if (status.IsError()) - return status; - - scoped_ptr<PublicKeyNss> key_handle( - new PublicKeyNss(sec_public_key.Pass(), CryptoData(spki_data))); - - *key = blink::WebCryptoKey::create(key_handle.release(), - blink::WebCryptoKeyTypePublic, extractable, - key_algorithm, usages); - - return Status::Success(); -} - -Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const { - if (key.type() != blink::WebCryptoKeyTypePrivate) - return Status::ErrorUnexpectedKeyType(); - *buffer = PrivateKeyNss::Cast(key)->pkcs8_data(); - return Status::Success(); -} - -Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const { - if (key.type() != blink::WebCryptoKeyTypePublic) - return Status::ErrorUnexpectedKeyType(); - *buffer = PublicKeyNss::Cast(key)->spki_data(); - return Status::Success(); -} - -Status RsaHashedAlgorithm::ImportKeyJwk( - const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const { - const char* jwk_algorithm = - GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); - - if (!jwk_algorithm) - return Status::ErrorUnexpected(); - - JwkRsaInfo jwk; - Status status = - ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk); - if (status.IsError()) - return status; - - // Once the key type is known, verify the usages. - status = CheckKeyCreationUsages( - jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, - usages, !jwk.is_private_key); - if (status.IsError()) - return status; - - return jwk.is_private_key - ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key) - : ImportRsaPublicKey(algorithm, extractable, usages, - CryptoData(jwk.n), CryptoData(jwk.e), key); -} - -Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const { - const char* jwk_algorithm = - GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); - - if (!jwk_algorithm) - return Status::ErrorUnexpected(); - - switch (key.type()) { - case blink::WebCryptoKeyTypePublic: { - SECKEYPublicKey* nss_key = PublicKeyNss::Cast(key)->key(); - if (nss_key->keyType != rsaKey) - return Status::ErrorUnsupported(); - - WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key->u.rsa.modulus), - SECItemToCryptoData(nss_key->u.rsa.publicExponent), - jwk_algorithm, key.extractable(), key.usages(), - buffer); - - return Status::Success(); - } - - case blink::WebCryptoKeyTypePrivate: { - SECKEYPrivateKey* nss_key = PrivateKeyNss::Cast(key)->key(); - RSAPrivateKey key_props = {}; - scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(&key_props); - - if (!InitRSAPrivateKey(nss_key, &key_props)) - return Status::OperationError(); - - WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props.modulus), - SECItemToCryptoData(key_props.public_exponent), - SECItemToCryptoData(key_props.private_exponent), - SECItemToCryptoData(key_props.prime1), - SECItemToCryptoData(key_props.prime2), - SECItemToCryptoData(key_props.exponent1), - SECItemToCryptoData(key_props.exponent2), - SECItemToCryptoData(key_props.coefficient), - jwk_algorithm, key.extractable(), key.usages(), - buffer); - - return Status::Success(); - } - default: - return Status::ErrorUnexpected(); - } -} - -Status RsaHashedAlgorithm::SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const { - key_data->assign(static_cast<KeyNss*>(key.handle())->serialized_key_data()); - return Status::Success(); -} - -// TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763 -Status RsaHashedAlgorithm::DeserializeKeyForClone( - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) const { - blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( - algorithm.id(), algorithm.rsaHashedParams()->hash().id()); - - Status status; - - switch (type) { - case blink::WebCryptoKeyTypePublic: - status = - ImportKeySpki(key_data, import_algorithm, extractable, usages, key); - break; - case blink::WebCryptoKeyTypePrivate: - status = - ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key); - break; - default: - return Status::ErrorUnexpected(); - } - - // There is some duplicated information in the serialized format used by - // structured clone (since the KeyAlgorithm is serialized separately from the - // key data). Use this extra information to further validate what was - // deserialized from the key data. - - if (algorithm.id() != key->algorithm().id()) - return Status::ErrorUnexpected(); - - if (key->type() != type) - return Status::ErrorUnexpected(); - - if (algorithm.rsaHashedParams()->modulusLengthBits() != - key->algorithm().rsaHashedParams()->modulusLengthBits()) { - return Status::ErrorUnexpected(); - } - - if (algorithm.rsaHashedParams()->publicExponent().size() != - key->algorithm().rsaHashedParams()->publicExponent().size() || - 0 != - memcmp(algorithm.rsaHashedParams()->publicExponent().data(), - key->algorithm().rsaHashedParams()->publicExponent().data(), - key->algorithm().rsaHashedParams()->publicExponent().size())) { - return Status::ErrorUnexpected(); - } - - return Status::Success(); -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.h b/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.h deleted file mode 100644 index ff8fbce6daf..00000000000 --- a/chromium/components/webcrypto/nss/rsa_hashed_algorithm_nss.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_NSS_RSA_HASHED_ALGORITHM_NSS_H_ -#define COMPONENTS_WEBCRYPTO_NSS_RSA_HASHED_ALGORITHM_NSS_H_ - -#include <pkcs11t.h> - -#include "components/webcrypto/algorithm_implementation.h" - -namespace webcrypto { - -class PublicKeyNss; -class PrivateKeyNss; - -// Base class for an RSA algorithm whose keys additionaly have a hash parameter -// bound to them. Provides functionality for generating, importing, and -// exporting keys. -class RsaHashedAlgorithm : public AlgorithmImplementation { - public: - // Constructs an RSA algorithm which will use the NSS flags |generate_flags| - // when generating keys. |all_public_key_usages| and |all_private_key_usages| - // are the set of WebCrypto key usages that are valid for created keys - // (public and private respectively). - // - // For instance if public keys support encryption and wrapping, and private - // keys support decryption and unwrapping callers should set: - // all_public_key_usages = UsageEncrypt | UsageWrap - // all_private_key_usages = UsageDecrypt | UsageUnwrap - // This information is used when importing or generating keys, to enforce - // that valid key usages are allowed. - RsaHashedAlgorithm(CK_FLAGS generate_flags, - blink::WebCryptoKeyUsageMask all_public_key_usages, - blink::WebCryptoKeyUsageMask all_private_key_usages) - : generate_flags_(generate_flags), - all_public_key_usages_(all_public_key_usages), - all_private_key_usages_(all_private_key_usages) {} - - // For instance "RSA-OAEP-256". - virtual const char* GetJwkAlgorithm( - const blink::WebCryptoAlgorithmId hash) const = 0; - - Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const override; - - Status VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const override; - - Status ImportKeyPkcs8(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override; - - Status ImportKeySpki(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override; - - Status ExportKeyPkcs8(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override; - - Status ExportKeySpki(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override; - - Status ImportKeyJwk(const CryptoData& key_data, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key) const override; - - Status ExportKeyJwk(const blink::WebCryptoKey& key, - std::vector<uint8_t>* buffer) const override; - - Status SerializeKeyForClone( - const blink::WebCryptoKey& key, - blink::WebVector<uint8_t>* key_data) const override; - - Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const CryptoData& key_data, - blink::WebCryptoKey* key) const override; - - private: - const CK_FLAGS generate_flags_; - const blink::WebCryptoKeyUsageMask all_public_key_usages_; - const blink::WebCryptoKeyUsageMask all_private_key_usages_; -}; - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_NSS_RSA_HASHED_ALGORITHM_NSS_H_ diff --git a/chromium/components/webcrypto/nss/rsa_oaep_nss.cc b/chromium/components/webcrypto/nss/rsa_oaep_nss.cc deleted file mode 100644 index d66b6c3b4ff..00000000000 --- a/chromium/components/webcrypto/nss/rsa_oaep_nss.cc +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <cryptohi.h> -#include <keyhi.h> -#include <pk11pub.h> -#include <secerr.h> -#include <sechash.h> - -#include "base/stl_util.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/rsa_hashed_algorithm_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -Status NssSupportsRsaOaep() { - if (NssRuntimeSupport::Get()->IsRsaOaepSupported()) - return Status::Success(); - return Status::ErrorUnsupported( - "NSS version doesn't support RSA-OAEP. Try using version 3.16.2 or " - "later"); -} - -CK_MECHANISM_TYPE WebCryptoHashToMGFMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - return CKG_MGF1_SHA1; - case blink::WebCryptoAlgorithmIdSha256: - return CKG_MGF1_SHA256; - case blink::WebCryptoAlgorithmIdSha384: - return CKG_MGF1_SHA384; - case blink::WebCryptoAlgorithmIdSha512: - return CKG_MGF1_SHA512; - default: - return CKM_INVALID_MECHANISM; - } -} - -CK_MECHANISM_TYPE WebCryptoHashToDigestMechanism( - const blink::WebCryptoAlgorithm& algorithm) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - return CKM_SHA_1; - case blink::WebCryptoAlgorithmIdSha256: - return CKM_SHA256; - case blink::WebCryptoAlgorithmIdSha384: - return CKM_SHA384; - case blink::WebCryptoAlgorithmIdSha512: - return CKM_SHA512; - default: - // Not a supported algorithm. - return CKM_INVALID_MECHANISM; - } -} - -bool InitializeRsaOaepParams(const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - CK_RSA_PKCS_OAEP_PARAMS* oaep_params) { - oaep_params->source = CKZ_DATA_SPECIFIED; - oaep_params->pSourceData = const_cast<unsigned char*>(label.bytes()); - oaep_params->ulSourceDataLen = label.byte_length(); - oaep_params->mgf = WebCryptoHashToMGFMechanism(hash); - oaep_params->hashAlg = WebCryptoHashToDigestMechanism(hash); - - if (oaep_params->mgf == CKM_INVALID_MECHANISM || - oaep_params->hashAlg == CKM_INVALID_MECHANISM) { - return false; - } - - return true; -} - -Status EncryptRsaOaep(SECKEYPublicKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8_t>* buffer) { - CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; - if (!InitializeRsaOaepParams(hash, label, &oaep_params)) - return Status::ErrorUnsupported(); - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&oaep_params); - param.len = sizeof(oaep_params); - - buffer->resize(SECKEY_PublicKeyStrength(key)); - unsigned char* buffer_data = vector_as_array(buffer); - unsigned int output_len; - if (NssRuntimeSupport::Get()->pk11_pub_encrypt_func()( - key, CKM_RSA_PKCS_OAEP, ¶m, buffer_data, &output_len, - buffer->size(), data.bytes(), data.byte_length(), - NULL) != SECSuccess) { - return Status::OperationError(); - } - - CHECK_LE(output_len, buffer->size()); - buffer->resize(output_len); - return Status::Success(); -} - -Status DecryptRsaOaep(SECKEYPrivateKey* key, - const blink::WebCryptoAlgorithm& hash, - const CryptoData& label, - const CryptoData& data, - std::vector<uint8_t>* buffer) { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - - CK_RSA_PKCS_OAEP_PARAMS oaep_params = {0}; - if (!InitializeRsaOaepParams(hash, label, &oaep_params)) - return Status::ErrorUnsupported(); - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&oaep_params); - param.len = sizeof(oaep_params); - - const int modulus_length_bytes = PK11_GetPrivateModulusLen(key); - if (modulus_length_bytes <= 0) - return Status::ErrorUnexpected(); - - buffer->resize(modulus_length_bytes); - - unsigned char* buffer_data = vector_as_array(buffer); - unsigned int output_len; - if (NssRuntimeSupport::Get()->pk11_priv_decrypt_func()( - key, CKM_RSA_PKCS_OAEP, ¶m, buffer_data, &output_len, - buffer->size(), data.bytes(), data.byte_length()) != SECSuccess) { - return Status::OperationError(); - } - - CHECK_LE(output_len, buffer->size()); - buffer->resize(output_len); - return Status::Success(); -} - -class RsaOaepImplementation : public RsaHashedAlgorithm { - public: - RsaOaepImplementation() - : RsaHashedAlgorithm( - CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP, - blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, - blink::WebCryptoKeyUsageDecrypt | - blink::WebCryptoKeyUsageUnwrapKey) {} - - Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - GenerateKeyResult* result) const override { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - return RsaHashedAlgorithm::GenerateKey(algorithm, extractable, usages, - result); - } - - Status VerifyKeyUsagesBeforeImportKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask usages) const override { - Status status = NssSupportsRsaOaep(); - if (status.IsError()) - return status; - return RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usages); - } - - const char* GetJwkAlgorithm( - const blink::WebCryptoAlgorithmId hash) const override { - switch (hash) { - case blink::WebCryptoAlgorithmIdSha1: - return "RSA-OAEP"; - case blink::WebCryptoAlgorithmIdSha256: - return "RSA-OAEP-256"; - case blink::WebCryptoAlgorithmIdSha384: - return "RSA-OAEP-384"; - case blink::WebCryptoAlgorithmIdSha512: - return "RSA-OAEP-512"; - default: - return NULL; - } - } - - Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - if (key.type() != blink::WebCryptoKeyTypePublic) - return Status::ErrorUnexpectedKeyType(); - - return EncryptRsaOaep( - PublicKeyNss::Cast(key)->key(), - key.algorithm().rsaHashedParams()->hash(), - CryptoData(algorithm.rsaOaepParams()->optionalLabel()), data, buffer); - } - - Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - if (key.type() != blink::WebCryptoKeyTypePrivate) - return Status::ErrorUnexpectedKeyType(); - - return DecryptRsaOaep( - PrivateKeyNss::Cast(key)->key(), - key.algorithm().rsaHashedParams()->hash(), - CryptoData(algorithm.rsaOaepParams()->optionalLabel()), data, buffer); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformRsaOaepImplementation() { - return new RsaOaepImplementation; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/rsa_ssa_nss.cc b/chromium/components/webcrypto/nss/rsa_ssa_nss.cc deleted file mode 100644 index 193bd040287..00000000000 --- a/chromium/components/webcrypto/nss/rsa_ssa_nss.cc +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <cryptohi.h> - -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/rsa_hashed_algorithm_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -class RsaSsaImplementation : public RsaHashedAlgorithm { - public: - RsaSsaImplementation() - : RsaHashedAlgorithm(CKF_SIGN | CKF_VERIFY, - blink::WebCryptoKeyUsageVerify, - blink::WebCryptoKeyUsageSign) {} - - const char* GetJwkAlgorithm( - const blink::WebCryptoAlgorithmId hash) const override { - switch (hash) { - case blink::WebCryptoAlgorithmIdSha1: - return "RS1"; - case blink::WebCryptoAlgorithmIdSha256: - return "RS256"; - case blink::WebCryptoAlgorithmIdSha384: - return "RS384"; - case blink::WebCryptoAlgorithmIdSha512: - return "RS512"; - default: - return NULL; - } - } - - Status Sign(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - if (key.type() != blink::WebCryptoKeyTypePrivate) - return Status::ErrorUnexpectedKeyType(); - - SECKEYPrivateKey* private_key = PrivateKeyNss::Cast(key)->key(); - - const blink::WebCryptoAlgorithm& hash = - key.algorithm().rsaHashedParams()->hash(); - - // Pick the NSS signing algorithm by combining RSA-SSA (RSA PKCS1) and the - // inner hash of the input Web Crypto algorithm. - SECOidTag sign_alg_tag; - switch (hash.id()) { - case blink::WebCryptoAlgorithmIdSha1: - sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha256: - sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha384: - sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; - break; - case blink::WebCryptoAlgorithmIdSha512: - sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; - break; - default: - return Status::ErrorUnsupported(); - } - - crypto::ScopedSECItem signature_item(SECITEM_AllocItem(NULL, NULL, 0)); - if (SEC_SignData(signature_item.get(), data.bytes(), data.byte_length(), - private_key, sign_alg_tag) != SECSuccess) { - return Status::OperationError(); - } - - buffer->assign(signature_item->data, - signature_item->data + signature_item->len); - return Status::Success(); - } - - Status Verify(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const CryptoData& signature, - const CryptoData& data, - bool* signature_match) const override { - if (key.type() != blink::WebCryptoKeyTypePublic) - return Status::ErrorUnexpectedKeyType(); - - SECKEYPublicKey* public_key = PublicKeyNss::Cast(key)->key(); - - const blink::WebCryptoAlgorithm& hash = - key.algorithm().rsaHashedParams()->hash(); - - const SECItem signature_item = MakeSECItemForBuffer(signature); - - SECOidTag hash_alg_tag; - switch (hash.id()) { - case blink::WebCryptoAlgorithmIdSha1: - hash_alg_tag = SEC_OID_SHA1; - break; - case blink::WebCryptoAlgorithmIdSha256: - hash_alg_tag = SEC_OID_SHA256; - break; - case blink::WebCryptoAlgorithmIdSha384: - hash_alg_tag = SEC_OID_SHA384; - break; - case blink::WebCryptoAlgorithmIdSha512: - hash_alg_tag = SEC_OID_SHA512; - break; - default: - return Status::ErrorUnsupported(); - } - - *signature_match = - SECSuccess == VFY_VerifyDataDirect(data.bytes(), data.byte_length(), - public_key, &signature_item, - SEC_OID_PKCS1_RSA_ENCRYPTION, - hash_alg_tag, NULL, NULL); - return Status::Success(); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformRsaSsaImplementation() { - return new RsaSsaImplementation; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/sha_nss.cc b/chromium/components/webcrypto/nss/sha_nss.cc deleted file mode 100644 index 17800a16cac..00000000000 --- a/chromium/components/webcrypto/nss/sha_nss.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <sechash.h> -#include <vector> - -#include "base/stl_util.h" -#include "components/webcrypto/algorithm_implementation.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/nss_util.h" -#include "crypto/scoped_nss_types.h" - -namespace webcrypto { - -namespace { - -HASH_HashType WebCryptoAlgorithmToNSSHashType( - blink::WebCryptoAlgorithmId algorithm) { - switch (algorithm) { - case blink::WebCryptoAlgorithmIdSha1: - return HASH_AlgSHA1; - case blink::WebCryptoAlgorithmIdSha256: - return HASH_AlgSHA256; - case blink::WebCryptoAlgorithmIdSha384: - return HASH_AlgSHA384; - case blink::WebCryptoAlgorithmIdSha512: - return HASH_AlgSHA512; - default: - // Not a digest algorithm. - return HASH_AlgNULL; - } -} - -// Implementation of blink::WebCryptoDigester, an internal Blink detail not -// part of WebCrypto, that allows chunks of data to be streamed in before -// computing a SHA-* digest (as opposed to ShaImplementation, which computes -// digests over complete messages) -class DigestorNSS : public blink::WebCryptoDigestor { - public: - explicit DigestorNSS(blink::WebCryptoAlgorithmId algorithm_id) - : hash_context_(NULL), algorithm_id_(algorithm_id) {} - - ~DigestorNSS() override { - if (!hash_context_) - return; - - HASH_Destroy(hash_context_); - hash_context_ = NULL; - } - - bool consume(const unsigned char* data, unsigned int size) override { - return ConsumeWithStatus(data, size).IsSuccess(); - } - - Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { - // Initialize everything if the object hasn't been initialized yet. - if (!hash_context_) { - Status error = Init(); - if (!error.IsSuccess()) - return error; - } - - HASH_Update(hash_context_, data, size); - - return Status::Success(); - } - - bool finish(unsigned char*& result_data, - unsigned int& result_data_size) override { - Status error = FinishInternal(result_, &result_data_size); - if (!error.IsSuccess()) - return false; - result_data = result_; - return true; - } - - Status FinishWithVectorAndStatus(std::vector<uint8_t>* result) { - if (!hash_context_) - return Status::ErrorUnexpected(); - - unsigned int result_length = HASH_ResultLenContext(hash_context_); - result->resize(result_length); - unsigned char* digest = vector_as_array(result); - unsigned int digest_size; // ignored - return FinishInternal(digest, &digest_size); - } - - private: - Status Init() { - HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm_id_); - - if (hash_type == HASH_AlgNULL) - return Status::ErrorUnsupported(); - - hash_context_ = HASH_Create(hash_type); - if (!hash_context_) - return Status::OperationError(); - - HASH_Begin(hash_context_); - - return Status::Success(); - } - - Status FinishInternal(unsigned char* result, unsigned int* result_size) { - if (!hash_context_) { - Status error = Init(); - if (!error.IsSuccess()) - return error; - } - - unsigned int hash_result_length = HASH_ResultLenContext(hash_context_); - DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX)); - - HASH_End(hash_context_, result, result_size, hash_result_length); - - if (*result_size != hash_result_length) - return Status::ErrorUnexpected(); - return Status::Success(); - } - - HASHContext* hash_context_; - blink::WebCryptoAlgorithmId algorithm_id_; - unsigned char result_[HASH_LENGTH_MAX]; -}; - -class ShaImplementation : public AlgorithmImplementation { - public: - Status Digest(const blink::WebCryptoAlgorithm& algorithm, - const CryptoData& data, - std::vector<uint8_t>* buffer) const override { - DigestorNSS digestor(algorithm.id()); - Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); - // http://crbug.com/366427: the spec does not define any other failures for - // digest, so none of the subsequent errors are spec compliant. - if (!error.IsSuccess()) - return error; - return digestor.FinishWithVectorAndStatus(buffer); - } -}; - -} // namespace - -AlgorithmImplementation* CreatePlatformShaImplementation() { - return new ShaImplementation(); -} - -scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( - blink::WebCryptoAlgorithmId algorithm) { - return scoped_ptr<blink::WebCryptoDigestor>(new DigestorNSS(algorithm)); -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/sym_key_nss.cc b/chromium/components/webcrypto/nss/sym_key_nss.cc deleted file mode 100644 index cd8f7fe08bd..00000000000 --- a/chromium/components/webcrypto/nss/sym_key_nss.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/nss/sym_key_nss.h" - -#include "base/logging.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/generate_key_result.h" -#include "components/webcrypto/nss/key_nss.h" -#include "components/webcrypto/nss/util_nss.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" -#include "crypto/scoped_nss_types.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -Status GenerateSecretKeyNss(const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - unsigned int keylen_bits, - CK_MECHANISM_TYPE mechanism, - GenerateKeyResult* result) { - DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); - - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - if (!slot) - return Status::OperationError(); - - std::vector<uint8_t> bytes(NumBitsToBytes(keylen_bits)); - if (bytes.size() > 0) { - if (PK11_GenerateRandom(&bytes[0], bytes.size()) != SECSuccess) - return Status::OperationError(); - TruncateToBitLength(keylen_bits, &bytes); - } - - blink::WebCryptoKey key; - Status status = ImportKeyRawNss(CryptoData(bytes), algorithm, extractable, - usages, mechanism, &key); - if (status.IsError()) - return status; - - result->AssignSecretKey(key); - return Status::Success(); -} - -Status ImportKeyRawNss(const CryptoData& key_data, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - CK_MECHANISM_TYPE mechanism, - blink::WebCryptoKey* key) { - // The usages are enforced at the WebCrypto layer, so it isn't necessary to - // create keys with limited usages. - CK_FLAGS flags = kAllOperationFlags; - - DCHECK(!algorithm.isNull()); - SECItem key_item = MakeSECItemForBuffer(key_data); - - crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); - crypto::ScopedPK11SymKey pk11_sym_key(PK11_ImportSymKeyWithFlags( - slot.get(), mechanism, PK11_OriginUnwrap, CKA_FLAGS_ONLY, &key_item, - flags, false, NULL)); - if (!pk11_sym_key.get()) - return Status::OperationError(); - - scoped_ptr<SymKeyNss> handle(new SymKeyNss(pk11_sym_key.Pass(), key_data)); - - *key = blink::WebCryptoKey::create(handle.release(), - blink::WebCryptoKeyTypeSecret, extractable, - algorithm, usages); - return Status::Success(); -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/sym_key_nss.h b/chromium/components/webcrypto/nss/sym_key_nss.h deleted file mode 100644 index 78d0acb9be1..00000000000 --- a/chromium/components/webcrypto/nss/sym_key_nss.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ -#define COMPONENTS_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ - -#include <pkcs11t.h> - -#include "third_party/WebKit/public/platform/WebCrypto.h" - -namespace webcrypto { - -class CryptoData; -class GenerateKeyResult; -class Status; - -Status GenerateSecretKeyNss(const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - unsigned int keylen_bits, - CK_MECHANISM_TYPE mechanism, - GenerateKeyResult* result); - -Status ImportKeyRawNss(const CryptoData& key_data, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - CK_MECHANISM_TYPE mechanism, - blink::WebCryptoKey* key); - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_NSS_SYM_KEY_NSS_H_ diff --git a/chromium/components/webcrypto/nss/util_nss.cc b/chromium/components/webcrypto/nss/util_nss.cc deleted file mode 100644 index 784a98016ca..00000000000 --- a/chromium/components/webcrypto/nss/util_nss.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/nss/util_nss.h" - -#include "base/lazy_instance.h" -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/platform_crypto.h" -#include "crypto/nss_util.h" -#include "crypto/scoped_nss_types.h" - -#if defined(USE_NSS_CERTS) -#include <dlfcn.h> -#include <secoid.h> -#endif - -namespace webcrypto { - -namespace { -base::LazyInstance<NssRuntimeSupport>::Leaky g_nss_runtime_support = - LAZY_INSTANCE_INITIALIZER; -} // namespace - -// Creates a SECItem for the data in |buffer|. This does NOT make a copy, so -// |buffer| should outlive the SECItem. -SECItem MakeSECItemForBuffer(const CryptoData& buffer) { - SECItem item = { - siBuffer, - // NSS requires non-const data even though it is just for input. - const_cast<unsigned char*>(buffer.bytes()), - buffer.byte_length()}; - return item; -} - -CryptoData SECItemToCryptoData(const SECItem& item) { - return CryptoData(item.data, item.len); -} - -NssRuntimeSupport* NssRuntimeSupport::Get() { - return &g_nss_runtime_support.Get(); -} - -NssRuntimeSupport::NssRuntimeSupport() : internal_slot_does_oaep_(false) { -#if !defined(USE_NSS_CERTS) - // Using a bundled version of NSS that is guaranteed to have this symbol. - pk11_encrypt_func_ = PK11_Encrypt; - pk11_decrypt_func_ = PK11_Decrypt; - pk11_pub_encrypt_func_ = PK11_PubEncrypt; - pk11_priv_decrypt_func_ = PK11_PrivDecrypt; - internal_slot_does_oaep_ = true; -#else - // Using system NSS libraries and PCKS #11 modules, which may not have the - // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM). - - // If PK11_Encrypt() was successfully resolved, then NSS will support - // AES-GCM directly. This was introduced in NSS 3.15. - pk11_encrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_Encrypt")); - pk11_decrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_Decrypt")); - - // Even though NSS's pk11wrap layer may support - // PK11_PubEncrypt/PK11_PubDecrypt (introduced in NSS 3.16.2), it may have - // loaded a softoken that does not include OAEP support. - pk11_pub_encrypt_func_ = reinterpret_cast<PK11_PubEncryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_PubEncrypt")); - pk11_priv_decrypt_func_ = reinterpret_cast<PK11_PrivDecryptFunction>( - dlsym(RTLD_DEFAULT, "PK11_PrivDecrypt")); - if (pk11_priv_decrypt_func_ && pk11_pub_encrypt_func_) { - crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); - internal_slot_does_oaep_ = - !!PK11_DoesMechanism(slot.get(), CKM_RSA_PKCS_OAEP); - } -#endif -} - -void PlatformInit() { - crypto::EnsureNSSInit(); -} - -AlgorithmImplementation* CreatePlatformAesCtrImplementation() { - // TODO(eroman): http://crbug.com/399084 - return NULL; -} - -AlgorithmImplementation* CreatePlatformRsaPssImplementation() { - // TODO(eroman): http://crbug.com/399090 - return NULL; -} - -AlgorithmImplementation* CreatePlatformEcdsaImplementation() { - // TODO(eroman): http://crbug.com/399094 - return NULL; -} - -AlgorithmImplementation* CreatePlatformEcdhImplementation() { - // TODO(eroman): http://crbug.com/399093 - return NULL; -} - -AlgorithmImplementation* CreatePlatformHkdfImplementation() { - // HKDF is only being imlemented for BoringSSL. - return NULL; -} - -AlgorithmImplementation* CreatePlatformPbkdf2Implementation() { - // PBKDF2 will only be implemented for BoringSSL, since the NSS - // implementation is being deprecated. - return NULL; -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/nss/util_nss.h b/chromium/components/webcrypto/nss/util_nss.h deleted file mode 100644 index 04a44ebd184..00000000000 --- a/chromium/components/webcrypto/nss/util_nss.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_NSS_UTIL_NSS_H_ -#define COMPONENTS_WEBCRYPTO_NSS_UTIL_NSS_H_ - -#include <keythi.h> -#include <pkcs11t.h> -#include <seccomon.h> -#include <secmodt.h> - -#include "base/lazy_instance.h" - -namespace webcrypto { - -class CryptoData; - -SECItem MakeSECItemForBuffer(const CryptoData& buffer); -enum EncryptOrDecrypt { ENCRYPT, DECRYPT }; - -CryptoData SECItemToCryptoData(const SECItem& item); - -const CK_FLAGS kAllOperationFlags = - CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP; - -// Signature for PK11_Encrypt and PK11_Decrypt. -typedef SECStatus (*PK11_EncryptDecryptFunction)(PK11SymKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int); - -// Signature for PK11_PubEncrypt -typedef SECStatus (*PK11_PubEncryptFunction)(SECKEYPublicKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int, - void*); - -// Signature for PK11_PrivDecrypt -typedef SECStatus (*PK11_PrivDecryptFunction)(SECKEYPrivateKey*, - CK_MECHANISM_TYPE, - SECItem*, - unsigned char*, - unsigned int*, - unsigned int, - const unsigned char*, - unsigned int); - -// Singleton that detects whether or not AES-GCM and -// RSA-OAEP are supported by the version of NSS being used. -// On non-Linux platforms, Chromium embedders ship with a -// fixed version of NSS, and these are always available. -// However, on Linux (and ChromeOS), NSS is provided by the -// system, and thus not all algorithms may be available -// or be safe to use. -class NssRuntimeSupport { - public: - bool IsAesGcmSupported() const { - return pk11_encrypt_func_ && pk11_decrypt_func_; - } - - bool IsRsaOaepSupported() const { - return pk11_pub_encrypt_func_ && pk11_priv_decrypt_func_ && - internal_slot_does_oaep_; - } - - // Returns NULL if unsupported. - PK11_EncryptDecryptFunction pk11_encrypt_func() const { - return pk11_encrypt_func_; - } - - // Returns NULL if unsupported. - PK11_EncryptDecryptFunction pk11_decrypt_func() const { - return pk11_decrypt_func_; - } - - // Returns NULL if unsupported. - PK11_PubEncryptFunction pk11_pub_encrypt_func() const { - return pk11_pub_encrypt_func_; - } - - // Returns NULL if unsupported. - PK11_PrivDecryptFunction pk11_priv_decrypt_func() const { - return pk11_priv_decrypt_func_; - } - - static NssRuntimeSupport* Get(); - - private: - friend struct base::DefaultLazyInstanceTraits<NssRuntimeSupport>; - - NssRuntimeSupport(); - - PK11_EncryptDecryptFunction pk11_encrypt_func_; - PK11_EncryptDecryptFunction pk11_decrypt_func_; - PK11_PubEncryptFunction pk11_pub_encrypt_func_; - PK11_PrivDecryptFunction pk11_priv_decrypt_func_; - bool internal_slot_does_oaep_; -}; - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_NSS_UTIL_NSS_H_ diff --git a/chromium/components/webcrypto/openssl/key_openssl.cc b/chromium/components/webcrypto/openssl/key_openssl.cc deleted file mode 100644 index 60922a47a82..00000000000 --- a/chromium/components/webcrypto/openssl/key_openssl.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/openssl/key_openssl.h" - -#include "components/webcrypto/crypto_data.h" -#include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" - -namespace webcrypto { - -KeyOpenSsl::KeyOpenSsl(const CryptoData& serialized_key_data) - : serialized_key_data_( - serialized_key_data.bytes(), - serialized_key_data.bytes() + serialized_key_data.byte_length()) { -} - -KeyOpenSsl::~KeyOpenSsl() { -} - -SymKeyOpenSsl* KeyOpenSsl::AsSymKey() { - return NULL; -} - -AsymKeyOpenSsl* KeyOpenSsl::AsAsymKey() { - return NULL; -} - -SymKeyOpenSsl::~SymKeyOpenSsl() { -} - -SymKeyOpenSsl* SymKeyOpenSsl::Cast(const blink::WebCryptoKey& key) { - KeyOpenSsl* platform_key = reinterpret_cast<KeyOpenSsl*>(key.handle()); - return platform_key->AsSymKey(); -} - -SymKeyOpenSsl* SymKeyOpenSsl::AsSymKey() { - return this; -} - -SymKeyOpenSsl::SymKeyOpenSsl(const CryptoData& raw_key_data) - : KeyOpenSsl(raw_key_data) { -} - -AsymKeyOpenSsl::~AsymKeyOpenSsl() { -} - -AsymKeyOpenSsl* AsymKeyOpenSsl::Cast(const blink::WebCryptoKey& key) { - return reinterpret_cast<KeyOpenSsl*>(key.handle())->AsAsymKey(); -} - -AsymKeyOpenSsl* AsymKeyOpenSsl::AsAsymKey() { - return this; -} - -AsymKeyOpenSsl::AsymKeyOpenSsl(crypto::ScopedEVP_PKEY key, - const CryptoData& serialized_key_data) - : KeyOpenSsl(serialized_key_data), key_(key.Pass()) { -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/openssl/key_openssl.h b/chromium/components/webcrypto/openssl/key_openssl.h deleted file mode 100644 index 8b5d6551569..00000000000 --- a/chromium/components/webcrypto/openssl/key_openssl.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ - -#include <openssl/ossl_typ.h> -#include <stdint.h> -#include <vector> - -#include "base/macros.h" -#include "crypto/scoped_openssl_types.h" -#include "third_party/WebKit/public/platform/WebCryptoKey.h" - -namespace webcrypto { - -class CryptoData; -class AsymKeyOpenSsl; -class SymKeyOpenSsl; - -// Base key class for all OpenSSL keys, used to safely cast between types. Each -// key maintains a copy of its serialized form in either 'raw', 'pkcs8', or -// 'spki' format. This is to allow structured cloning of keys synchronously from -// the target Blink thread without having to lock access to the key. -class KeyOpenSsl : public blink::WebCryptoKeyHandle { - public: - explicit KeyOpenSsl(const CryptoData& serialized_key_data); - ~KeyOpenSsl() override; - - virtual SymKeyOpenSsl* AsSymKey(); - virtual AsymKeyOpenSsl* AsAsymKey(); - - const std::vector<uint8_t>& serialized_key_data() const { - return serialized_key_data_; - } - - private: - const std::vector<uint8_t> serialized_key_data_; -}; - -class SymKeyOpenSsl : public KeyOpenSsl { - public: - ~SymKeyOpenSsl() override; - explicit SymKeyOpenSsl(const CryptoData& raw_key_data); - - static SymKeyOpenSsl* Cast(const blink::WebCryptoKey& key); - - SymKeyOpenSsl* AsSymKey() override; - - const std::vector<uint8_t>& raw_key_data() const { - return serialized_key_data(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(SymKeyOpenSsl); -}; - -class AsymKeyOpenSsl : public KeyOpenSsl { - public: - ~AsymKeyOpenSsl() override; - AsymKeyOpenSsl(crypto::ScopedEVP_PKEY key, - const CryptoData& serialized_key_data); - - static AsymKeyOpenSsl* Cast(const blink::WebCryptoKey& key); - - AsymKeyOpenSsl* AsAsymKey() override; - - EVP_PKEY* key() { return key_.get(); } - - private: - crypto::ScopedEVP_PKEY key_; - - DISALLOW_COPY_AND_ASSIGN(AsymKeyOpenSsl); -}; - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_KEY_OPENSSL_H_ diff --git a/chromium/components/webcrypto/openssl/util_openssl.h b/chromium/components/webcrypto/openssl/util_openssl.h deleted file mode 100644 index eef51a4cc87..00000000000 --- a/chromium/components/webcrypto/openssl/util_openssl.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ -#define COMPONENTS_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ - -#include <string> -#include <vector> - -#include <openssl/ossl_typ.h> - -#include "crypto/scoped_openssl_types.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoKey.h" - -namespace webcrypto { - -class CryptoData; -class GenerateKeyResult; -class Status; - -// The values of these constants correspond with the "enc" parameter of -// EVP_CipherInit_ex(), do not change. -enum EncryptOrDecrypt { DECRYPT = 0, ENCRYPT = 1 }; - -const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id); - -// Does either encryption or decryption for an AEAD algorithm. -// * |mode| controls whether encryption or decryption is done -// * |aead_alg| the algorithm (for instance AES-GCM) -// * |buffer| where the ciphertext or plaintext is written to. -Status AeadEncryptDecrypt(EncryptOrDecrypt mode, - const std::vector<uint8_t>& raw_key, - const CryptoData& data, - unsigned int tag_length_bytes, - const CryptoData& iv, - const CryptoData& additional_data, - const EVP_AEAD* aead_alg, - std::vector<uint8_t>* buffer); - -// Generates a random secret key of the given bit length. If the bit length is -// not a multiple of 8, then the resulting key will have ceil(keylen_bits / 8) -// bytes, and the "unused" bits will be set to zero. This function does not do -// any validation checks on the provided parameters. -Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - unsigned int keylen_bits, - GenerateKeyResult* result); - -// Creates a WebCrypto secret key given a the raw data. The provided |key_data| -// will be copied into the new key. This function does not do any validation -// checks for the provided parameters. -Status CreateWebCryptoSecretKey(const CryptoData& key_data, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key); - -// Creates a WebCrypto public key given an EVP_PKEY. This step includes -// exporting the key to SPKI format, for use by serialization later. -Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key); - -// Creates a WebCrypto private key given an EVP_PKEY. This step includes -// exporting the key to PKCS8 format, for use by serialization later. -Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, - const blink::WebCryptoKeyAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoKey* key); - -// Imports SPKI bytes to an EVP_PKEY for a public key. The resulting asymmetric -// key may be invalid, and should be verified using something like -// RSA_check_key(). The only validation performed by this function is to ensure -// the key type matched |expected_pkey_id|. -Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, - int expected_pkey_id, - crypto::ScopedEVP_PKEY* pkey); - -// Imports PKCS8 bytes to an EVP_PKEY for a private key. The resulting -// asymmetric key may be invalid, and should be verified using something like -// RSA_check_key(). The only validation performed by this function is to ensure -// the key type matched |expected_pkey_id|. -Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, - int expected_pkey_id, - crypto::ScopedEVP_PKEY* pkey); - -// Allocates a new BIGNUM given a std::string big-endian representation. -BIGNUM* CreateBIGNUM(const std::string& n); - -// Converts a BIGNUM to a big endian byte array. -std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n); - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_OPENSSL_UTIL_OPENSSL_H_ diff --git a/chromium/components/webcrypto/platform_crypto.h b/chromium/components/webcrypto/platform_crypto.h deleted file mode 100644 index fdb8cef4a71..00000000000 --- a/chromium/components/webcrypto/platform_crypto.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_PLATFORM_CRYPTO_H_ -#define COMPONENTS_WEBCRYPTO_PLATFORM_CRYPTO_H_ - -#include <stdint.h> -#include <vector> - -#include "base/memory/scoped_ptr.h" -#include "third_party/WebKit/public/platform/WebCrypto.h" - -// The definitions for these methods lives in either nss/ or openssl/ -namespace webcrypto { - -class AlgorithmImplementation; - -void PlatformInit(); - -scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor( - blink::WebCryptoAlgorithmId algorithm); - -AlgorithmImplementation* CreatePlatformShaImplementation(); -AlgorithmImplementation* CreatePlatformAesCbcImplementation(); -AlgorithmImplementation* CreatePlatformAesCtrImplementation(); -AlgorithmImplementation* CreatePlatformAesGcmImplementation(); -AlgorithmImplementation* CreatePlatformAesKwImplementation(); -AlgorithmImplementation* CreatePlatformHmacImplementation(); -AlgorithmImplementation* CreatePlatformRsaOaepImplementation(); -AlgorithmImplementation* CreatePlatformRsaSsaImplementation(); -AlgorithmImplementation* CreatePlatformRsaPssImplementation(); -AlgorithmImplementation* CreatePlatformEcdsaImplementation(); -AlgorithmImplementation* CreatePlatformEcdhImplementation(); -AlgorithmImplementation* CreatePlatformHkdfImplementation(); -AlgorithmImplementation* CreatePlatformPbkdf2Implementation(); - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_PLATFORM_CRYPTO_H_ diff --git a/chromium/components/webcrypto/status.h b/chromium/components/webcrypto/status.h index 7e0ba9790f1..94124db6ade 100644 --- a/chromium/components/webcrypto/status.h +++ b/chromium/components/webcrypto/status.h @@ -134,7 +134,7 @@ class Status { // Attempted to generate an AES key with an invalid length. static Status ErrorGenerateAesKeyLength(); - // 192-bit AES keys are valid, however unsupported. + // 192-bit AES keys are valid, however unsupported (http://crbug.com/533699) static Status ErrorAes192BitUnsupported(); // The wrong key was used for the operation. For instance, a public key was diff --git a/chromium/components/webcrypto/status_unittest.cc b/chromium/components/webcrypto/status_unittest.cc new file mode 100644 index 00000000000..ad79136f5ef --- /dev/null +++ b/chromium/components/webcrypto/status_unittest.cc @@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stl_util.h" +#include "components/webcrypto/algorithm_dispatch.h" +#include "components/webcrypto/algorithms/test_helpers.h" +#include "components/webcrypto/crypto_data.h" +#include "components/webcrypto/status.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" + +namespace webcrypto { + +namespace { + +// Tests several Status objects against their expected hard coded values, as +// well as ensuring that comparison of Status objects works. +// Comparison should take into account both the error details, as well as the +// error type. +TEST(WebCryptoStatusTest, Basic) { + // Even though the error message is the same, these should not be considered + // the same by the tests because the error type is different. + EXPECT_NE(Status::DataError(), Status::OperationError()); + EXPECT_NE(Status::Success(), Status::OperationError()); + + EXPECT_EQ(Status::Success(), Status::Success()); + EXPECT_EQ(Status::ErrorJwkMemberWrongType("kty", "string"), + Status::ErrorJwkMemberWrongType("kty", "string")); + + Status status = Status::Success(); + + EXPECT_FALSE(status.IsError()); + EXPECT_EQ("", status.error_details()); + + status = Status::OperationError(); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ("", status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeOperation, status.error_type()); + + status = Status::DataError(); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ("", status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); + + status = Status::ErrorUnsupported(); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ("The requested operation is unsupported", status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeNotSupported, status.error_type()); + + status = Status::ErrorJwkMemberMissing("kty"); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ("The required JWK member \"kty\" was missing", + status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); + + status = Status::ErrorJwkMemberWrongType("kty", "string"); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ("The JWK member \"kty\" must be a string", status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); + + status = Status::ErrorJwkBase64Decode("n"); + EXPECT_TRUE(status.IsError()); + EXPECT_EQ( + "The JWK member \"n\" could not be base64url decoded or contained " + "padding", + status.error_details()); + EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); +} + +} // namespace + +} // namespace webcrypto diff --git a/chromium/components/webcrypto/webcrypto.gyp b/chromium/components/webcrypto/webcrypto.gyp index f386343e3b4..e4cf9af5507 100644 --- a/chromium/components/webcrypto/webcrypto.gyp +++ b/chromium/components/webcrypto/webcrypto.gyp @@ -9,120 +9,60 @@ 'type': 'static_library', 'dependencies': [ '../../base/base.gyp:base', - '../tracing.gyp:tracing', - '../../skia/skia.gyp:skia', - '../../ui/base/ui_base.gyp:ui_base', - '../../ui/events/events.gyp:gestures_blink', - '../../url/url.gyp:url_lib', + '../../crypto/crypto.gyp:crypto', + '../../third_party/boringssl/boringssl.gyp:boringssl', + '../../third_party/WebKit/public/blink.gyp:blink', ], 'include_dirs': [ '..', ], - 'export_dependent_settings': [ - '../../base/base.gyp:base', - ], - 'variables': { - 'webcrypto_sources': [ - 'algorithm_dispatch.cc', - 'algorithm_dispatch.h', - 'algorithm_implementation.cc', - 'algorithm_implementation.h', - 'algorithm_registry.cc', - 'algorithm_registry.h', - 'crypto_data.cc', - 'crypto_data.h', - 'generate_key_result.cc', - 'generate_key_result.h', - 'jwk.cc', - 'jwk.h', - 'platform_crypto.h', - 'status.cc', - 'status.h', - 'webcrypto_impl.cc', - 'webcrypto_impl.h', - 'webcrypto_util.cc', - 'webcrypto_util.h', - ], - 'webcrypto_nss_sources': [ - 'nss/aes_algorithm_nss.cc', - 'nss/aes_algorithm_nss.h', - 'nss/aes_cbc_nss.cc', - 'nss/aes_gcm_nss.cc', - 'nss/aes_kw_nss.cc', - 'nss/hmac_nss.cc', - 'nss/key_nss.cc', - 'nss/key_nss.h', - 'nss/rsa_hashed_algorithm_nss.cc', - 'nss/rsa_hashed_algorithm_nss.h', - 'nss/rsa_oaep_nss.cc', - 'nss/rsa_ssa_nss.cc', - 'nss/sha_nss.cc', - 'nss/sym_key_nss.cc', - 'nss/sym_key_nss.h', - 'nss/util_nss.cc', - 'nss/util_nss.h', - ], - 'webcrypto_openssl_sources': [ - 'openssl/aes_algorithm_openssl.cc', - 'openssl/aes_algorithm_openssl.h', - 'openssl/aes_cbc_openssl.cc', - 'openssl/aes_ctr_openssl.cc', - 'openssl/aes_gcm_openssl.cc', - 'openssl/aes_kw_openssl.cc', - 'openssl/ec_algorithm_openssl.cc', - 'openssl/ec_algorithm_openssl.h', - 'openssl/ecdh_openssl.cc', - 'openssl/ecdsa_openssl.cc', - 'openssl/hkdf_openssl.cc', - 'openssl/hmac_openssl.cc', - 'openssl/key_openssl.cc', - 'openssl/key_openssl.h', - 'openssl/pbkdf2_openssl.cc', - 'openssl/rsa_hashed_algorithm_openssl.cc', - 'openssl/rsa_hashed_algorithm_openssl.h', - 'openssl/rsa_oaep_openssl.cc', - 'openssl/rsa_pss_openssl.cc', - 'openssl/rsa_sign_openssl.cc', - 'openssl/rsa_sign_openssl.h', - 'openssl/rsa_ssa_openssl.cc', - 'openssl/sha_openssl.cc', - 'openssl/util_openssl.cc', - 'openssl/util_openssl.h', - ], - }, 'sources': [ - '<@(webcrypto_sources)', - ], - 'conditions': [ - ['OS=="android"', { - 'dependencies': [ - '../../build/android/ndk.gyp:cpu_features', - ], - }], - ['use_openssl==1', { - 'sources': [ - '<@(webcrypto_openssl_sources)', - ], - 'dependencies': [ - '../../third_party/boringssl/boringssl.gyp:boringssl', - ], - }, { - 'sources': [ - '<@(webcrypto_nss_sources)', - ], - 'conditions': [ - ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', { - 'dependencies': [ - '../../build/linux/system.gyp:ssl', - ], - }, { - 'dependencies': [ - '../../third_party/nss/nss.gyp:nspr', - '../../third_party/nss/nss.gyp:nss', - ], - }], - ], - }], + 'algorithm_dispatch.cc', + 'algorithm_dispatch.h', + 'algorithm_implementation.cc', + 'algorithm_implementation.h', + 'algorithm_implementations.h', + 'algorithm_registry.cc', + 'algorithm_registry.h', + 'algorithms/aes.cc', + 'algorithms/aes.h', + 'algorithms/aes_cbc.cc', + 'algorithms/aes_ctr.cc', + 'algorithms/aes_gcm.cc', + 'algorithms/aes_kw.cc', + 'algorithms/asymmetric_key_util.cc', + 'algorithms/asymmetric_key_util.h', + 'algorithms/ec.cc', + 'algorithms/ec.h', + 'algorithms/ecdh.cc', + 'algorithms/ecdsa.cc', + 'algorithms/hkdf.cc', + 'algorithms/hmac.cc', + 'algorithms/pbkdf2.cc', + 'algorithms/rsa.cc', + 'algorithms/rsa.h', + 'algorithms/rsa_oaep.cc', + 'algorithms/rsa_pss.cc', + 'algorithms/rsa_sign.cc', + 'algorithms/rsa_sign.h', + 'algorithms/rsa_ssa.cc', + 'algorithms/secret_key_util.cc', + 'algorithms/secret_key_util.h', + 'algorithms/sha.cc', + 'algorithms/util.cc', + 'algorithms/util.h', + 'blink_key_handle.cc', + 'blink_key_handle.h', + 'crypto_data.cc', + 'crypto_data.h', + 'generate_key_result.cc', + 'generate_key_result.h', + 'jwk.cc', + 'jwk.h', + 'status.cc', + 'status.h', + 'webcrypto_impl.cc', + 'webcrypto_impl.h', ], }, ], diff --git a/chromium/components/webcrypto/webcrypto_impl.cc b/chromium/components/webcrypto/webcrypto_impl.cc index 3ccfbc912d1..71d4b2b26da 100644 --- a/chromium/components/webcrypto/webcrypto_impl.cc +++ b/chromium/components/webcrypto/webcrypto_impl.cc @@ -19,7 +19,6 @@ #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/generate_key_result.h" #include "components/webcrypto/status.h" -#include "components/webcrypto/webcrypto_util.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -63,7 +62,7 @@ namespace { // threads is silly. // // * WebCryptoAlgorithm and WebCryptoKey are threadsafe (however the key's -// handle(), which wraps an NSS/OpenSSL type, may not be and should only be +// handle(), which wraps an OpenSSL type, may not be and should only be // used from the webcrypto thread). // // * blink::WebCryptoResult is not threadsafe and should only be operated on diff --git a/chromium/components/webcrypto/webcrypto_impl.h b/chromium/components/webcrypto/webcrypto_impl.h index 8b24b53b677..068a65cfc18 100644 --- a/chromium/components/webcrypto/webcrypto_impl.h +++ b/chromium/components/webcrypto/webcrypto_impl.h @@ -14,7 +14,7 @@ namespace webcrypto { // Wrapper around the Blink WebCrypto asynchronous interface, which forwards to -// the synchronous platform (NSS or OpenSSL) implementation. +// the synchronous OpenSSL implementation. // // WebCryptoImpl is threadsafe. // @@ -23,79 +23,75 @@ class WebCryptoImpl : public blink::WebCrypto { public: WebCryptoImpl(); - // TODO(eroman): Once Blink and Chromium repositories are merged, use - // "override" in place of virtual. + ~WebCryptoImpl() override; - virtual ~WebCryptoImpl(); - - virtual void encrypt(const blink::WebCryptoAlgorithm& algorithm, + void encrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const unsigned char* data, + unsigned int data_size, + blink::WebCryptoResult result) override; + void decrypt(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const unsigned char* data, + unsigned int data_size, + blink::WebCryptoResult result) override; + void digest(const blink::WebCryptoAlgorithm& algorithm, + const unsigned char* data, + unsigned int data_size, + blink::WebCryptoResult result) override; + void generateKey(const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoResult result) override; + void importKey(blink::WebCryptoKeyFormat format, + const unsigned char* key_data, + unsigned int key_data_size, + const blink::WebCryptoAlgorithm& algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoResult result) override; + void exportKey(blink::WebCryptoKeyFormat format, + const blink::WebCryptoKey& key, + blink::WebCryptoResult result) override; + void sign(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& key, + const unsigned char* data, + unsigned int data_size, + blink::WebCryptoResult result) override; + void verifySignature(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, + const unsigned char* signature, + unsigned int signature_size, const unsigned char* data, unsigned int data_size, - blink::WebCryptoResult result); - virtual void decrypt(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const unsigned char* data, - unsigned int data_size, - blink::WebCryptoResult result); - virtual void digest(const blink::WebCryptoAlgorithm& algorithm, - const unsigned char* data, - unsigned int data_size, - blink::WebCryptoResult result); - virtual void generateKey(const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoResult result); - virtual void importKey(blink::WebCryptoKeyFormat format, - const unsigned char* key_data, - unsigned int key_data_size, - const blink::WebCryptoAlgorithm& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoResult result); - virtual void exportKey(blink::WebCryptoKeyFormat format, - const blink::WebCryptoKey& key, - blink::WebCryptoResult result); - virtual void sign(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const unsigned char* data, - unsigned int data_size, - blink::WebCryptoResult result); - virtual void verifySignature(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& key, - const unsigned char* signature, - unsigned int signature_size, - const unsigned char* data, - unsigned int data_size, - blink::WebCryptoResult result); - virtual void wrapKey(blink::WebCryptoKeyFormat format, - const blink::WebCryptoKey& key, - const blink::WebCryptoKey& wrapping_key, - const blink::WebCryptoAlgorithm& wrap_algorithm, - blink::WebCryptoResult result); - virtual void unwrapKey( - blink::WebCryptoKeyFormat format, - const unsigned char* wrapped_key, - unsigned wrapped_key_size, - const blink::WebCryptoKey& wrapping_key, - const blink::WebCryptoAlgorithm& unwrap_algorithm, - const blink::WebCryptoAlgorithm& unwrapped_key_algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoResult result); + blink::WebCryptoResult result) override; + void wrapKey(blink::WebCryptoKeyFormat format, + const blink::WebCryptoKey& key, + const blink::WebCryptoKey& wrapping_key, + const blink::WebCryptoAlgorithm& wrap_algorithm, + blink::WebCryptoResult result) override; + void unwrapKey(blink::WebCryptoKeyFormat format, + const unsigned char* wrapped_key, + unsigned wrapped_key_size, + const blink::WebCryptoKey& wrapping_key, + const blink::WebCryptoAlgorithm& unwrap_algorithm, + const blink::WebCryptoAlgorithm& unwrapped_key_algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoResult result) override; - virtual void deriveBits(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& base_key, - unsigned int length_bits, - blink::WebCryptoResult result); + void deriveBits(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& base_key, + unsigned int length_bits, + blink::WebCryptoResult result) override; - virtual void deriveKey(const blink::WebCryptoAlgorithm& algorithm, - const blink::WebCryptoKey& base_key, - const blink::WebCryptoAlgorithm& import_algorithm, - const blink::WebCryptoAlgorithm& key_length_algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - blink::WebCryptoResult result); + void deriveKey(const blink::WebCryptoAlgorithm& algorithm, + const blink::WebCryptoKey& base_key, + const blink::WebCryptoAlgorithm& import_algorithm, + const blink::WebCryptoAlgorithm& key_length_algorithm, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + blink::WebCryptoResult result) override; // This method returns a digestor object that can be used to synchronously // compute a digest one chunk at a time. Thus, the consume does not need to @@ -103,20 +99,19 @@ class WebCryptoImpl : public blink::WebCrypto { // one at a time and the digest will be computed piecemeal. The allocated // WebCrytpoDigestor that is returned by createDigestor must be freed by the // caller. - virtual blink::WebCryptoDigestor* createDigestor( - blink::WebCryptoAlgorithmId algorithm_id); + blink::WebCryptoDigestor* createDigestor( + blink::WebCryptoAlgorithmId algorithm_id) override; - virtual bool deserializeKeyForClone( - const blink::WebCryptoKeyAlgorithm& algorithm, - blink::WebCryptoKeyType type, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - const unsigned char* key_data, - unsigned key_data_size, - blink::WebCryptoKey& key); + bool deserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, + blink::WebCryptoKeyType type, + bool extractable, + blink::WebCryptoKeyUsageMask usages, + const unsigned char* key_data, + unsigned key_data_size, + blink::WebCryptoKey& key) override; - virtual bool serializeKeyForClone(const blink::WebCryptoKey& key, - blink::WebVector<unsigned char>& key_data); + bool serializeKeyForClone(const blink::WebCryptoKey& key, + blink::WebVector<unsigned char>& key_data) override; private: DISALLOW_COPY_AND_ASSIGN(WebCryptoImpl); diff --git a/chromium/components/webcrypto/webcrypto_util.cc b/chromium/components/webcrypto/webcrypto_util.cc deleted file mode 100644 index 2e80c1f8afe..00000000000 --- a/chromium/components/webcrypto/webcrypto_util.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/webcrypto/webcrypto_util.h" - -#include "base/logging.h" -#include "base/numerics/safe_math.h" -#include "components/webcrypto/status.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" -#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" - -namespace webcrypto { - -namespace { - -// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, -// to unsigned int. -bool BigIntegerToUint(const uint8_t* data, - size_t data_size, - unsigned int* result) { - if (data_size == 0) - return false; - - *result = 0; - for (size_t i = 0; i < data_size; ++i) { - size_t reverse_i = data_size - i - 1; - - if (reverse_i >= sizeof(*result) && data[i]) - return false; // Too large for a uint. - - *result |= data[i] << 8 * reverse_i; - } - return true; -} - -Status GetShaBlockSizeBits(const blink::WebCryptoAlgorithm& algorithm, - unsigned int* block_size_bits) { - switch (algorithm.id()) { - case blink::WebCryptoAlgorithmIdSha1: - case blink::WebCryptoAlgorithmIdSha256: - *block_size_bits = 512; - return Status::Success(); - case blink::WebCryptoAlgorithmIdSha384: - case blink::WebCryptoAlgorithmIdSha512: - *block_size_bits = 1024; - return Status::Success(); - default: - return Status::ErrorUnsupported(); - } -} - -} // namespace - -blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) { - return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL); -} - -blink::WebCryptoAlgorithm CreateHmacImportAlgorithm( - blink::WebCryptoAlgorithmId hash_id, - unsigned int length_bits) { - DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdHmac, - new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id), true, - length_bits)); -} - -blink::WebCryptoAlgorithm CreateHmacImportAlgorithmNoLength( - blink::WebCryptoAlgorithmId hash_id) { - DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - blink::WebCryptoAlgorithmIdHmac, - new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id), false, 0)); -} - -blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmId id, - blink::WebCryptoAlgorithmId hash_id) { - DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id))); -} - -blink::WebCryptoAlgorithm CreateEcImportAlgorithm( - blink::WebCryptoAlgorithmId id, - blink::WebCryptoNamedCurve named_curve) { - return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - id, new blink::WebCryptoEcKeyImportParams(named_curve)); -} - -bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, - blink::WebCryptoKeyUsageMask b) { - return (a & b) == b; -} - -// TODO(eroman): Move this helper to WebCryptoKey. -bool KeyUsageAllows(const blink::WebCryptoKey& key, - const blink::WebCryptoKeyUsage usage) { - return ((key.usages() & usage) != 0); -} - -// The WebCrypto spec defines the default value for the tag length, as well as -// the allowed values for tag length. -Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params, - unsigned int* tag_length_bits) { - *tag_length_bits = 128; - if (params->hasTagLengthBits()) - *tag_length_bits = params->optionalTagLengthBits(); - - if (*tag_length_bits != 32 && *tag_length_bits != 64 && - *tag_length_bits != 96 && *tag_length_bits != 104 && - *tag_length_bits != 112 && *tag_length_bits != 120 && - *tag_length_bits != 128) - return Status::ErrorInvalidAesGcmTagLength(); - - return Status::Success(); -} - -Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params, - unsigned int* keylen_bits) { - *keylen_bits = params->lengthBits(); - - if (*keylen_bits == 128 || *keylen_bits == 256) - return Status::Success(); - - // BoringSSL does not support 192-bit AES. - if (*keylen_bits == 192) - return Status::ErrorAes192BitUnsupported(); - - return Status::ErrorGenerateAesKeyLength(); -} - -Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params, - unsigned int* keylen_bits) { - if (!params->hasLengthBits()) - return GetShaBlockSizeBits(params->hash(), keylen_bits); - - *keylen_bits = params->optionalLengthBits(); - - // Zero-length HMAC keys are disallowed by the spec. - if (*keylen_bits == 0) - return Status::ErrorGenerateHmacKeyLengthZero(); - - return Status::Success(); -} - -Status GetHmacImportKeyLengthBits( - const blink::WebCryptoHmacImportParams* params, - unsigned int key_data_byte_length, - unsigned int* keylen_bits) { - if (key_data_byte_length == 0) - return Status::ErrorHmacImportEmptyKey(); - - // Make sure that the key data's length can be represented in bits without - // overflow. - base::CheckedNumeric<unsigned int> checked_keylen_bits(key_data_byte_length); - checked_keylen_bits *= 8; - - if (!checked_keylen_bits.IsValid()) - return Status::ErrorDataTooLarge(); - - unsigned int data_keylen_bits = checked_keylen_bits.ValueOrDie(); - - // Determine how many bits of the input to use. - *keylen_bits = data_keylen_bits; - if (params->hasLengthBits()) { - // The requested bit length must be: - // * No longer than the input data length - // * At most 7 bits shorter. - if (NumBitsToBytes(params->optionalLengthBits()) != key_data_byte_length) - return Status::ErrorHmacImportBadLength(); - *keylen_bits = params->optionalLengthBits(); - } - - return Status::Success(); -} - -Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes) { - if (keylen_bytes == 16 || keylen_bytes == 32) - return Status::Success(); - - // BoringSSL does not support 192-bit AES. - if (keylen_bytes == 24) - return Status::ErrorAes192BitUnsupported(); - - return Status::ErrorImportAesKeyLength(); -} - -Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, - blink::WebCryptoKeyUsageMask actual_usages, - bool allow_empty_usages) { - if (!allow_empty_usages && actual_usages == 0) - return Status::ErrorCreateKeyEmptyUsages(); - - if (!ContainsKeyUsages(all_possible_usages, actual_usages)) - return Status::ErrorCreateKeyBadUsages(); - return Status::Success(); -} - -Status GetRsaKeyGenParameters( - const blink::WebCryptoRsaHashedKeyGenParams* params, - unsigned int* public_exponent, - unsigned int* modulus_length_bits) { - *modulus_length_bits = params->modulusLengthBits(); - - // Limit key sizes to those supported by NSS: - // * Multiple of 8 bits - // * 256 bits to 16K bits - if (*modulus_length_bits < 256 || *modulus_length_bits > 16384 || - (*modulus_length_bits % 8) != 0) { - return Status::ErrorGenerateRsaUnsupportedModulus(); - } - - if (!BigIntegerToUint(params->publicExponent().data(), - params->publicExponent().size(), public_exponent)) { - return Status::ErrorGenerateKeyPublicExponent(); - } - - // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To - // avoid feeding OpenSSL data that will hang use a whitelist. - if (*public_exponent != 3 && *public_exponent != 65537) - return Status::ErrorGenerateKeyPublicExponent(); - - return Status::Success(); -} - -Status VerifyUsagesBeforeImportAsymmetricKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask all_public_key_usages, - blink::WebCryptoKeyUsageMask all_private_key_usages, - blink::WebCryptoKeyUsageMask usages) { - switch (format) { - case blink::WebCryptoKeyFormatSpki: - return CheckKeyCreationUsages(all_public_key_usages, usages, true); - case blink::WebCryptoKeyFormatPkcs8: - return CheckKeyCreationUsages(all_private_key_usages, usages, false); - case blink::WebCryptoKeyFormatJwk: { - // The JWK could represent either a public key or private key. The usages - // must make sense for one of the two. The usages will be checked again by - // ImportKeyJwk() once the key type has been determined. - if (CheckKeyCreationUsages(all_public_key_usages, usages, true) - .IsError() && - CheckKeyCreationUsages(all_private_key_usages, usages, false) - .IsError()) { - return Status::ErrorCreateKeyBadUsages(); - } - return Status::Success(); - } - default: - return Status::ErrorUnsupportedImportKeyFormat(); - } -} - -void TruncateToBitLength(size_t length_bits, std::vector<uint8_t>* bytes) { - size_t length_bytes = NumBitsToBytes(length_bits); - - if (bytes->size() != length_bytes) { - CHECK_LT(length_bytes, bytes->size()); - bytes->resize(length_bytes); - } - - size_t remainder_bits = length_bits % 8; - - // Zero any "unused bits" in the final byte - if (remainder_bits) - (*bytes)[bytes->size() - 1] &= ~((0xFF) >> remainder_bits); -} - -Status GetAesKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits) { - const blink::WebCryptoAesDerivedKeyParams* params = - key_length_algorithm.aesDerivedKeyParams(); - - *has_length_bits = true; - *length_bits = params->lengthBits(); - - if (*length_bits == 128 || *length_bits == 256) - return Status::Success(); - - // BoringSSL does not support 192-bit AES. - if (*length_bits == 192) - return Status::ErrorAes192BitUnsupported(); - - return Status::ErrorGetAesKeyLength(); -} - -Status GetHmacKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits) { - const blink::WebCryptoHmacImportParams* params = - key_length_algorithm.hmacImportParams(); - - if (params->hasLengthBits()) { - *has_length_bits = true; - *length_bits = params->optionalLengthBits(); - if (*length_bits == 0) - return Status::ErrorGetHmacKeyLengthZero(); - return Status::Success(); - } - - *has_length_bits = true; - return GetShaBlockSizeBits(params->hash(), length_bits); -} - -Status GetUsagesForGenerateAsymmetricKey( - blink::WebCryptoKeyUsageMask combined_usages, - blink::WebCryptoKeyUsageMask all_public_usages, - blink::WebCryptoKeyUsageMask all_private_usages, - blink::WebCryptoKeyUsageMask* public_usages, - blink::WebCryptoKeyUsageMask* private_usages) { - Status status = CheckKeyCreationUsages(all_public_usages | all_private_usages, - combined_usages, true); - if (status.IsError()) - return status; - - *public_usages = combined_usages & all_public_usages; - *private_usages = combined_usages & all_private_usages; - - if (*private_usages == 0) - return Status::ErrorCreateKeyEmptyUsages(); - - return Status::Success(); -} - -} // namespace webcrypto diff --git a/chromium/components/webcrypto/webcrypto_util.h b/chromium/components/webcrypto/webcrypto_util.h deleted file mode 100644 index ab711ab7a2c..00000000000 --- a/chromium/components/webcrypto/webcrypto_util.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_WEBCRYPTO_WEBCRYPTO_UTIL_H_ -#define COMPONENTS_WEBCRYPTO_WEBCRYPTO_UTIL_H_ - -#include <stdint.h> -#include <string> - -#include "base/values.h" -#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" -#include "third_party/WebKit/public/platform/WebCryptoKey.h" - -namespace webcrypto { - -class Status; - -// Creates a WebCryptoAlgorithm without any parameters. -blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id); - -// Creates an HMAC import algorithm whose inner hash algorithm is determined by -// the specified algorithm ID. It is an error to call this method with a hash -// algorithm that is not SHA*. -blink::WebCryptoAlgorithm CreateHmacImportAlgorithm( - blink::WebCryptoAlgorithmId hash_id, - unsigned int length_bits); - -// Same as above but without specifying a length. -blink::WebCryptoAlgorithm CreateHmacImportAlgorithmNoLength( - blink::WebCryptoAlgorithmId hash_id); - -// Creates an import algorithm for RSA algorithms that take a hash. -// It is an error to call this with a hash_id that is not a SHA*. -blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( - blink::WebCryptoAlgorithmId id, - blink::WebCryptoAlgorithmId hash_id); - -// Creates an import algorithm for EC keys. -blink::WebCryptoAlgorithm CreateEcImportAlgorithm( - blink::WebCryptoAlgorithmId id, - blink::WebCryptoNamedCurve named_curve); - -// Returns true if the set bits in b make up a subset of the set bits in a. -bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, - blink::WebCryptoKeyUsageMask b); - -bool KeyUsageAllows(const blink::WebCryptoKey& key, - const blink::WebCryptoKeyUsage usage); - -Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params, - unsigned int* tag_length_bits); - -Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params, - unsigned int* keylen_bits); - -Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params, - unsigned int* keylen_bits); - -// Gets the requested key length in bits for an HMAC import operation. -Status GetHmacImportKeyLengthBits( - const blink::WebCryptoHmacImportParams* params, - unsigned int key_data_byte_length, - unsigned int* keylen_bits); - -Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes); - -Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages, - blink::WebCryptoKeyUsageMask actual_usages, - bool allow_empty_usages); - -// Extracts the public exponent and modulus length from the Blink parameters. -// On success it is guaranteed that: -// * public_exponent is either 3 or 65537 -// * modulus_length_bits is a multiple of 8 -// * modulus_length is >= 256 -// * modulus_length is <= 16K -Status GetRsaKeyGenParameters( - const blink::WebCryptoRsaHashedKeyGenParams* params, - unsigned int* public_exponent, - unsigned int* modulus_length_bits); - -// Verifies that |usages| is valid when importing a key of the given format. -Status VerifyUsagesBeforeImportAsymmetricKey( - blink::WebCryptoKeyFormat format, - blink::WebCryptoKeyUsageMask all_public_key_usages, - blink::WebCryptoKeyUsageMask all_private_key_usages, - blink::WebCryptoKeyUsageMask usages); - -// Truncates an octet string to a particular bit length. This is accomplished by -// resizing to the closest byte length, and then zero-ing the unused -// least-significant bits of the final byte. -// -// It is an error to call this function with a bit length that is larger than -// that of |bytes|. -// -// TODO(eroman): This operation is not yet defined by the WebCrypto spec, -// however this is a reasonable interpretation: -// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27402 -void TruncateToBitLength(size_t length_bits, std::vector<uint8_t>* bytes); - -// Rounds a bit count (up) to the nearest byte count. -// -// This is mathematically equivalent to (x + 7) / 8, however has no -// possibility of integer overflow. -template <typename T> -T NumBitsToBytes(T x) { - return (x / 8) + (7 + (x % 8)) / 8; -} - -// The "get key length" operation for AES keys. -Status GetAesKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits); - -// The "get key length" operation for HMAC keys. -Status GetHmacKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, - bool* has_length_bits, - unsigned int* length_bits); - -// Splits the combined usages given to GenerateKey() into the respective usages -// for the public key and private key. Returns an error if the usages are -// invalid. -Status GetUsagesForGenerateAsymmetricKey( - blink::WebCryptoKeyUsageMask combined_usages, - blink::WebCryptoKeyUsageMask all_public_usages, - blink::WebCryptoKeyUsageMask all_private_usages, - blink::WebCryptoKeyUsageMask* public_usages, - blink::WebCryptoKeyUsageMask* private_usages); - -} // namespace webcrypto - -#endif // COMPONENTS_WEBCRYPTO_WEBCRYPTO_UTIL_H_ |