// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/351564777): Remove this and convert code to safer constructs. #pragma allow_unsafe_buffers #endif #include "crypto/hmac.h" #include #include #include #include "base/check.h" #include "base/check_op.h" #include "base/notreached.h" #include "base/stl_util.h" #include "crypto/openssl_util.h" #include "crypto/secure_util.h" #include "crypto/symmetric_key.h" #include "third_party/boringssl/src/include/openssl/hmac.h" namespace crypto { HMAC::HMAC(HashAlgorithm hash_alg) : hash_alg_(hash_alg), initialized_(false) { // Only SHA-1 and SHA-256 hash algorithms are supported now. DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); } HMAC::~HMAC() { // Zero out key copy. key_.assign(key_.size(), 0); base::STLClearObject(&key_); } size_t HMAC::DigestLength() const { switch (hash_alg_) { case SHA1: return 20; case SHA256: return 32; default: NOTREACHED(); } } bool HMAC::Init(const unsigned char* key, size_t key_length) { // Init must not be called more than once on the same HMAC object. DCHECK(!initialized_); initialized_ = true; key_.assign(key, key + key_length); return true; } bool HMAC::Init(const SymmetricKey* key) { return Init(key->key()); } bool HMAC::Sign(std::string_view data, unsigned char* digest, size_t digest_length) const { return Sign(base::as_byte_span(data), base::span(digest, digest_length)); } bool HMAC::Sign(base::span data, base::span digest) const { DCHECK(initialized_); if (digest.size() > DigestLength()) return false; ScopedOpenSSLSafeSizeBuffer result(digest.data(), digest.size()); return !!::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), key_.data(), key_.size(), data.data(), data.size(), result.safe_buffer(), nullptr); } bool HMAC::Verify(std::string_view data, std::string_view digest) const { return Verify(base::as_byte_span(data), base::as_byte_span(digest)); } bool HMAC::Verify(base::span data, base::span digest) const { if (digest.size() != DigestLength()) return false; return VerifyTruncated(data, digest); } bool HMAC::VerifyTruncated(std::string_view data, std::string_view digest) const { return VerifyTruncated(base::as_byte_span(data), base::as_byte_span(digest)); } bool HMAC::VerifyTruncated(base::span data, base::span digest) const { if (digest.empty()) return false; size_t digest_length = DigestLength(); if (digest.size() > digest_length) return false; std::array computed_buffer; auto computed_digest = base::span(computed_buffer).first(digest.size()); if (!Sign(data, computed_digest)) { return false; } return SecureMemEqual(digest, computed_digest); } namespace hmac { namespace { const EVP_MD* EVPMDForHashKind(crypto::hash::HashKind kind) { switch (kind) { case crypto::hash::HashKind::kSha1: return EVP_sha1(); case crypto::hash::HashKind::kSha256: return EVP_sha256(); case crypto::hash::HashKind::kSha512: return EVP_sha512(); } NOTREACHED(); } } // namespace void Sign(crypto::hash::HashKind kind, base::span key, base::span data, base::span hmac) { const EVP_MD* md = EVPMDForHashKind(kind); CHECK_EQ(hmac.size(), EVP_MD_size(md)); bssl::ScopedHMAC_CTX ctx; CHECK(HMAC_Init_ex(ctx.get(), key.data(), key.size(), EVPMDForHashKind(kind), nullptr)); CHECK(HMAC_Update(ctx.get(), data.data(), data.size())); CHECK(HMAC_Final(ctx.get(), hmac.data(), nullptr)); } bool Verify(crypto::hash::HashKind kind, base::span key, base::span data, base::span hmac) { const EVP_MD* md = EVPMDForHashKind(kind); CHECK_EQ(hmac.size(), EVP_MD_size(md)); std::array computed_buf; base::span computed = base::span(computed_buf).first(EVP_MD_size(md)); Sign(kind, key, data, computed); return crypto::SecureMemEqual(computed, hmac); } std::array SignSha1( base::span key, base::span data) { std::array result; Sign(crypto::hash::HashKind::kSha1, key, data, result); return result; } std::array SignSha256( base::span key, base::span data) { std::array result; Sign(crypto::hash::HashKind::kSha256, key, data, result); return result; } std::array SignSha512( base::span key, base::span data) { std::array result; Sign(crypto::hash::HashKind::kSha512, key, data, result); return result; } bool VerifySha1(base::span key, base::span data, base::span hmac) { return Verify(crypto::hash::HashKind::kSha1, key, data, hmac); } bool VerifySha256(base::span key, base::span data, base::span hmac) { return Verify(crypto::hash::HashKind::kSha256, key, data, hmac); } bool VerifySha512(base::span key, base::span data, base::span hmac) { return Verify(crypto::hash::HashKind::kSha512, key, data, hmac); } } // namespace hmac } // namespace crypto