// 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 #include #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/numerics/safe_math.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/status.h" #include "crypto/openssl_util.h" #include "crypto/secure_util.h" #include "third_party/blink/public/platform/web_crypto_algorithm_params.h" #include "third_party/blink/public/platform/web_crypto_key_algorithm.h" #include "third_party/boringssl/src/include/openssl/hmac.h" 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(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 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::kWebCryptoAlgorithmIdSha1: return "HS1"; case blink::kWebCryptoAlgorithmIdSha256: return "HS256"; case blink::kWebCryptoAlgorithmIdSha384: return "HS384"; case blink::kWebCryptoAlgorithmIdSha512: return "HS512"; default: return nullptr; } } const blink::WebCryptoKeyUsageMask kAllKeyUsages = blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify; Status SignHmac(const std::vector& raw_key, const blink::WebCryptoAlgorithm& hash, const CryptoData& data, std::vector* buffer) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); const EVP_MD* digest_algorithm = GetDigest(hash); if (!digest_algorithm) return Status::ErrorUnsupported(); size_t hmac_expected_length = EVP_MD_size(digest_algorithm); buffer->resize(hmac_expected_length); unsigned int hmac_actual_length; if (!HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.bytes(), data.byte_length(), buffer->data(), &hmac_actual_length)) { return Status::OperationError(); } // HMAC() promises to use at most EVP_MD_CTX_size(). If this was not the // case then memory corruption may have just occurred. CHECK_EQ(hmac_expected_length, hmac_actual_length); return Status::Success(); } 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); if (status.IsError()) return status; const blink::WebCryptoHmacKeyGenParams* params = algorithm.HmacKeyGenParams(); unsigned int keylen_bits = 0; 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->GetHash(), &keylen_bits); if (status.IsError()) return status; } return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::CreateHmac( params->GetHash().Id(), keylen_bits), extractable, usages, keylen_bits, result); } Status ImportKey(blink::WebCryptoKeyFormat format, const CryptoData& key_data, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const override { switch (format) { case blink::kWebCryptoKeyFormatRaw: return ImportKeyRaw(key_data, algorithm, extractable, usages, key); case blink::kWebCryptoKeyFormatJwk: return ImportKeyJwk(key_data, algorithm, extractable, usages, key); default: return Status::ErrorUnsupportedImportKeyFormat(); } } Status ExportKey(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& key, std::vector* buffer) const override { switch (format) { case blink::kWebCryptoKeyFormatRaw: return ExportKeyRaw(key, buffer); case blink::kWebCryptoKeyFormatJwk: return ExportKeyJwk(key, buffer); default: return Status::ErrorUnsupportedExportKeyFormat(); } } Status ImportKeyRaw(const CryptoData& key_data, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const { Status status = CheckKeyCreationUsages(kAllKeyUsages, usages); if (status.IsError()) return status; const blink::WebCryptoHmacImportParams* params = algorithm.HmacImportParams(); unsigned int keylen_bits = 0; status = GetHmacImportKeyLengthBits(params, key_data.byte_length(), &keylen_bits); if (status.IsError()) return status; const blink::WebCryptoKeyAlgorithm key_algorithm = blink::WebCryptoKeyAlgorithm::CreateHmac(params->GetHash().Id(), keylen_bits); // If no bit truncation was requested, then done! if ((keylen_bits % 8) == 0) { return CreateWebCryptoSecretKey(key_data, key_algorithm, extractable, usages, key); } // Otherwise zero out the unused bits in the key data before importing. std::vector modified_key_data( key_data.bytes(), key_data.bytes() + key_data.byte_length()); TruncateToBitLength(keylen_bits, &modified_key_data); return CreateWebCryptoSecretKey(CryptoData(modified_key_data), key_algorithm, extractable, usages, key); } Status ImportKeyJwk(const CryptoData& key_data, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const { Status status = CheckKeyCreationUsages(kAllKeyUsages, usages); if (status.IsError()) return status; const char* algorithm_name = GetJwkHmacAlgorithmName(algorithm.HmacImportParams()->GetHash().Id()); if (!algorithm_name) return Status::ErrorUnexpected(); std::vector raw_data; JwkReader jwk; status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages, &raw_data, &jwk); if (status.IsError()) return status; status = jwk.VerifyAlg(algorithm_name); if (status.IsError()) return status; return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, key); } Status ExportKeyRaw(const blink::WebCryptoKey& key, std::vector* buffer) const { *buffer = GetSymmetricKeyData(key); return Status::Success(); } Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector* buffer) const { const std::vector& raw_data = GetSymmetricKeyData(key); const char* algorithm_name = GetJwkHmacAlgorithmName(key.Algorithm().HmacParams()->GetHash().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* buffer) const override { const blink::WebCryptoAlgorithm& hash = key.Algorithm().HmacParams()->GetHash(); return SignHmac(GetSymmetricKeyData(key), hash, data, buffer); } Status Verify(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& signature, const CryptoData& data, bool* signature_match) const override { std::vector 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(result.data(), signature.bytes(), signature.byte_length()); 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 { if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeHmac || type != blink::kWebCryptoKeyTypeSecret) return Status::ErrorUnexpected(); return CreateWebCryptoSecretKey(key_data, algorithm, extractable, usages, key); } Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm, bool* has_length_bits, unsigned int* length_bits) const override { 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->GetHash(), length_bits); } }; } // namespace std::unique_ptr CreateHmacImplementation() { return base::WrapUnique(new HmacImplementation); } } // namespace webcrypto