// 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 #include "base/logging.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/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.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::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; 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); crypto::ScopedOpenSSLSafeSizeBuffer hmac_result( buffer->data(), hmac_expected_length); unsigned int hmac_actual_length; unsigned char* const success = HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.bytes(), data.byte_length(), hmac_result.safe_buffer(), &hmac_actual_length); if (!success || hmac_actual_length != hmac_expected_length) return Status::OperationError(); 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 = CheckSecretKeyCreationUsages(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->hash(), &keylen_bits); if (status.IsError()) return status; } return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::createHmac( params->hash().id(), keylen_bits), extractable, usages, keylen_bits, result); } Status VerifyKeyUsagesBeforeImportKey( blink::WebCryptoKeyFormat format, blink::WebCryptoKeyUsageMask usages) const override { switch (format) { case blink::WebCryptoKeyFormatRaw: case blink::WebCryptoKeyFormatJwk: return CheckSecretKeyCreationUsages(kAllKeyUsages, usages); 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(); 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 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 override { const char* algorithm_name = GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); if (!algorithm_name) return Status::ErrorUnexpected(); std::vector 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; return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, key); } Status ExportKeyRaw(const blink::WebCryptoKey& key, std::vector* buffer) const override { *buffer = GetSymmetricKeyData(key); return Status::Success(); } Status ExportKeyJwk(const blink::WebCryptoKey& key, std::vector* buffer) const override { const std::vector& raw_data = GetSymmetricKeyData(key); 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* buffer) const override { const blink::WebCryptoAlgorithm& hash = key.algorithm().hmacParams()->hash(); 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 { 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->hash(), length_bits); } }; } // namespace scoped_ptr CreateHmacImplementation() { return make_scoped_ptr(new HmacImplementation); } } // namespace webcrypto