| // Copyright 2026 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "crypto/default/hasher.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/base/no_destructor.h" |
| #include "absl/base/nullability.h" |
| #include "absl/log/absl_log.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/status_macros.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "crypto/algorithms.h" |
| #include "crypto/hash.h" |
| #include "openssl/base.h" |
| #include "openssl/digest.h" |
| |
| namespace credentio { |
| namespace { |
| |
| class DefaultHashChecker : public HashChecker { |
| public: |
| explicit DefaultHashChecker(std::unique_ptr<Hasher> hasher) |
| : hasher_(std::move(hasher)) {} |
| |
| void Update(absl::string_view content) override { hasher_->Update(content); } |
| |
| bool Check(absl::string_view expected_hash) override { |
| std::string computed_hash = hasher_->Digest(); |
| if (computed_hash != expected_hash) { |
| ABSL_DVLOG(1) << "Hash mismatch: base64(computed) = " |
| << absl::Base64Escape(computed_hash) |
| << " base64(expected) = " |
| << absl::Base64Escape(expected_hash); |
| return false; |
| } |
| return true; |
| } |
| |
| private: |
| std::unique_ptr<Hasher> hasher_; |
| }; |
| |
| class DefaultHashCheckerFactoryImpl : public HashCheckerFactory { |
| public: |
| DefaultHashCheckerFactoryImpl() = default; |
| |
| absl::StatusOr<std::unique_ptr<HashChecker>> Create( |
| HashAlgorithm algorithm) const override { |
| ABSL_ASSIGN_OR_RETURN(auto hasher, CreateHasher(algorithm)); |
| return std::make_unique<DefaultHashChecker>(std::move(hasher)); |
| } |
| }; |
| |
| class DefaultHasher : public Hasher { |
| public: |
| static absl::StatusOr<std::unique_ptr<DefaultHasher>> Create( |
| const EVP_MD* md) { |
| bssl::UniquePtr<EVP_MD_CTX> ctx(EVP_MD_CTX_new()); |
| if (ctx == nullptr || EVP_DigestInit_ex(ctx.get(), md, nullptr) != 1) { |
| return absl::InternalError("Failed to initialize hasher"); |
| } |
| return std::unique_ptr<DefaultHasher>(new DefaultHasher(std::move(ctx))); |
| } |
| |
| void Update(absl::string_view content) override { |
| // We don't check the return value because BoringSSL's implementation always |
| // returns 1. |
| EVP_DigestUpdate(ctx_.get(), content.data(), content.size()); |
| } |
| |
| std::string Digest() override { |
| unsigned int len = 0; |
| unsigned char digest[EVP_MAX_MD_SIZE]; |
| // We don't check the return value because BoringSSL's implementation always |
| // returns 1. |
| EVP_DigestFinal_ex(ctx_.get(), digest, &len); |
| return std::string(reinterpret_cast<char*>(digest), len); |
| } |
| |
| private: |
| explicit DefaultHasher(bssl::UniquePtr<EVP_MD_CTX> ctx) |
| : ctx_(std::move(ctx)) {} |
| |
| bssl::UniquePtr<EVP_MD_CTX> ctx_; |
| }; |
| |
| } // namespace |
| |
| absl::StatusOr<std::unique_ptr<Hasher> absl_nonnull> CreateHasher( |
| HashAlgorithm algorithm) { |
| switch (algorithm) { |
| case HashAlgorithm::kSha256: |
| return DefaultHasher::Create(EVP_sha256()); |
| case HashAlgorithm::kSha384: |
| return DefaultHasher::Create(EVP_sha384()); |
| case HashAlgorithm::kSha512: |
| return DefaultHasher::Create(EVP_sha512()); |
| } |
| std::string error_msg = absl::StrCat( |
| "fell through end of exhaustive switch statement with HashAlgorithm " |
| "value: ", |
| static_cast<int>(algorithm)); |
| ABSL_LOG(DFATAL) << error_msg; |
| return absl::FailedPreconditionError(error_msg); |
| } |
| |
| absl::StatusOr<std::unique_ptr<Hasher> absl_nonnull> CreateHasher( |
| absl::string_view algorithm) { |
| ABSL_ASSIGN_OR_RETURN(auto ha, ParseHashAlgorithm(algorithm)); |
| return CreateHasher(ha); |
| } |
| |
| const HashCheckerFactory& DefaultHashCheckerFactory() { |
| static const absl::NoDestructor<DefaultHashCheckerFactoryImpl> singleton; |
| return *singleton; |
| } |
| |
| const HasherFactory& DefaultHasherFactory() { |
| static const absl::NoDestructor<Sha256HasherFactory> singleton; |
| return *singleton; |
| } |
| |
| absl::StatusOr<std::unique_ptr<Hasher>> Sha256HasherFactory::Create() const { |
| return DefaultHasher::Create(EVP_sha256()); |
| } |
| |
| absl::StatusOr<std::unique_ptr<Hasher>> Sha384HasherFactory::Create() const { |
| return DefaultHasher::Create(EVP_sha384()); |
| } |
| |
| absl::StatusOr<std::unique_ptr<Hasher>> Sha512HasherFactory::Create() const { |
| return DefaultHasher::Create(EVP_sha512()); |
| } |
| |
| } // namespace credentio |