From a5be6de770c9152f3248ce0f01ac3645e30a19ed Mon Sep 17 00:00:00 2001 From: Maksim Denisov Date: Wed, 17 Jul 2024 16:26:25 +0200 Subject: Use compile-time polymorphism in Hasher ...since runtime polymorphism was there just to avoid including openssl as a public dependency. A combination of forward declarations, std::variant and std::unique_ptr is used instead. --- src/buildtool/crypto/TARGETS | 15 +-- src/buildtool/crypto/hash_impl_sha1.cpp | 54 --------- src/buildtool/crypto/hash_impl_sha1.hpp | 25 ----- src/buildtool/crypto/hash_impl_sha256.cpp | 54 --------- src/buildtool/crypto/hash_impl_sha256.hpp | 25 ----- src/buildtool/crypto/hash_impl_sha512.cpp | 54 --------- src/buildtool/crypto/hash_impl_sha512.hpp | 25 ----- src/buildtool/crypto/hasher.cpp | 179 ++++++++++++++++++++++++++---- src/buildtool/crypto/hasher.hpp | 46 +++----- 9 files changed, 176 insertions(+), 301 deletions(-) delete mode 100644 src/buildtool/crypto/hash_impl_sha1.cpp delete mode 100644 src/buildtool/crypto/hash_impl_sha1.hpp delete mode 100644 src/buildtool/crypto/hash_impl_sha256.cpp delete mode 100644 src/buildtool/crypto/hash_impl_sha256.hpp delete mode 100644 src/buildtool/crypto/hash_impl_sha512.cpp delete mode 100644 src/buildtool/crypto/hash_impl_sha512.hpp (limited to 'src') diff --git a/src/buildtool/crypto/TARGETS b/src/buildtool/crypto/TARGETS index 6890b54f..fba8ba84 100644 --- a/src/buildtool/crypto/TARGETS +++ b/src/buildtool/crypto/TARGETS @@ -1,22 +1,13 @@ { "hasher": { "type": ["@", "rules", "CC", "library"] , "name": ["hasher"] - , "hdrs": - [ "hasher.hpp" - , "hash_impl_sha1.hpp" - , "hash_impl_sha256.hpp" - , "hash_impl_sha512.hpp" - ] - , "srcs": - [ "hasher.cpp" - , "hash_impl_sha1.cpp" - , "hash_impl_sha256.cpp" - , "hash_impl_sha512.cpp" - ] + , "hdrs": ["hasher.hpp"] + , "srcs": ["hasher.cpp"] , "stage": ["src", "buildtool", "crypto"] , "deps": [["src/utils/cpp", "hex_string"]] , "private-deps": [ ["@", "ssl", "", "crypto"] + , ["@", "gsl", "", "gsl"] , ["src/buildtool/logging", "log_level"] , ["src/buildtool/logging", "logging"] ] diff --git a/src/buildtool/crypto/hash_impl_sha1.cpp b/src/buildtool/crypto/hash_impl_sha1.cpp deleted file mode 100644 index c72046d1..00000000 --- a/src/buildtool/crypto/hash_impl_sha1.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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 "src/buildtool/crypto/hash_impl_sha1.hpp" - -#include -#include - -#include "openssl/sha.h" - -/// \brief Hash implementation for SHA-1 -class HashImplSha1 final : public Hasher::IHashImpl { - public: - HashImplSha1() { initialized_ = SHA1_Init(&ctx_) == 1; } - - auto Update(std::string const& data) noexcept -> bool final { - return initialized_ and - SHA1_Update(&ctx_, data.data(), data.size()) == 1; - } - - auto Finalize() && noexcept -> std::optional final { - if (initialized_) { - auto out = std::array{}; - if (SHA1_Final(out.data(), &ctx_) == 1) { - return std::string{out.begin(), out.end()}; - } - } - return std::nullopt; - } - - [[nodiscard]] auto GetHashLength() const noexcept -> size_t final { - return SHA_DIGEST_LENGTH * kCharsPerNumber; - } - - private: - SHA_CTX ctx_{}; - bool initialized_{}; -}; - -/// \brief Factory for SHA-1 implementation -auto CreateHashImplSha1() -> std::unique_ptr { - return std::make_unique(); -} diff --git a/src/buildtool/crypto/hash_impl_sha1.hpp b/src/buildtool/crypto/hash_impl_sha1.hpp deleted file mode 100644 index 0a46599a..00000000 --- a/src/buildtool/crypto/hash_impl_sha1.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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. - -#ifndef INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA1_HPP -#define INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA1_HPP - -#include - -#include "src/buildtool/crypto/hasher.hpp" - -[[nodiscard]] extern auto CreateHashImplSha1() - -> std::unique_ptr; - -#endif // INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA1_HPP diff --git a/src/buildtool/crypto/hash_impl_sha256.cpp b/src/buildtool/crypto/hash_impl_sha256.cpp deleted file mode 100644 index 8aa4d04f..00000000 --- a/src/buildtool/crypto/hash_impl_sha256.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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 "src/buildtool/crypto/hash_impl_sha256.hpp" - -#include -#include - -#include "openssl/sha.h" - -/// \brief Hash implementation for SHA-256 -class HashImplSha256 final : public Hasher::IHashImpl { - public: - HashImplSha256() { initialized_ = SHA256_Init(&ctx_) == 1; } - - auto Update(std::string const& data) noexcept -> bool final { - return initialized_ and - SHA256_Update(&ctx_, data.data(), data.size()) == 1; - } - - auto Finalize() && noexcept -> std::optional final { - if (initialized_) { - auto out = std::array{}; - if (SHA256_Final(out.data(), &ctx_) == 1) { - return std::string{out.begin(), out.end()}; - } - } - return std::nullopt; - } - - [[nodiscard]] auto GetHashLength() const noexcept -> size_t final { - return SHA256_DIGEST_LENGTH * kCharsPerNumber; - } - - private: - SHA256_CTX ctx_{}; - bool initialized_{}; -}; - -/// \brief Factory for SHA-256 implementation -auto CreateHashImplSha256() -> std::unique_ptr { - return std::make_unique(); -} diff --git a/src/buildtool/crypto/hash_impl_sha256.hpp b/src/buildtool/crypto/hash_impl_sha256.hpp deleted file mode 100644 index e9142ac6..00000000 --- a/src/buildtool/crypto/hash_impl_sha256.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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. - -#ifndef INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA256_HPP -#define INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA256_HPP - -#include - -#include "src/buildtool/crypto/hasher.hpp" - -[[nodiscard]] extern auto CreateHashImplSha256() - -> std::unique_ptr; - -#endif // INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA256_HPP diff --git a/src/buildtool/crypto/hash_impl_sha512.cpp b/src/buildtool/crypto/hash_impl_sha512.cpp deleted file mode 100644 index 524c966c..00000000 --- a/src/buildtool/crypto/hash_impl_sha512.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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 "src/buildtool/crypto/hash_impl_sha512.hpp" - -#include -#include - -#include "openssl/sha.h" - -/// \brief Hash implementation for SHA-512 -class HashImplSha512 final : public Hasher::IHashImpl { - public: - HashImplSha512() { initialized_ = SHA512_Init(&ctx_) == 1; } - - auto Update(std::string const& data) noexcept -> bool final { - return initialized_ and - SHA512_Update(&ctx_, data.data(), data.size()) == 1; - } - - auto Finalize() && noexcept -> std::optional final { - if (initialized_) { - auto out = std::array{}; - if (SHA512_Final(out.data(), &ctx_) == 1) { - return std::string{out.begin(), out.end()}; - } - } - return std::nullopt; - } - - [[nodiscard]] auto GetHashLength() const noexcept -> size_t final { - return SHA512_DIGEST_LENGTH * kCharsPerNumber; - } - - private: - SHA512_CTX ctx_{}; - bool initialized_{}; -}; - -/// \brief Factory for SHA-512 implementation -auto CreateHashImplSha512() -> std::unique_ptr { - return std::make_unique(); -} diff --git a/src/buildtool/crypto/hash_impl_sha512.hpp b/src/buildtool/crypto/hash_impl_sha512.hpp deleted file mode 100644 index 8a32cc42..00000000 --- a/src/buildtool/crypto/hash_impl_sha512.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// 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 -// -// http://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. - -#ifndef INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA512_HPP -#define INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA512_HPP - -#include - -#include "src/buildtool/crypto/hasher.hpp" - -[[nodiscard]] extern auto CreateHashImplSha512() - -> std::unique_ptr; - -#endif // INCLUDED_SRC_BUILDTOOL_CRYPTO_HASH_IMPL_SHA512_HPP diff --git a/src/buildtool/crypto/hasher.cpp b/src/buildtool/crypto/hasher.cpp index 6e2f6ee3..41182559 100644 --- a/src/buildtool/crypto/hasher.cpp +++ b/src/buildtool/crypto/hasher.cpp @@ -14,51 +14,188 @@ #include "src/buildtool/crypto/hasher.hpp" -#include "src/buildtool/crypto/hash_impl_sha1.hpp" -#include "src/buildtool/crypto/hash_impl_sha256.hpp" -#include "src/buildtool/crypto/hash_impl_sha512.hpp" +#include +#include +#include +#include + +#include "gsl/gsl" +#include "openssl/sha.h" #include "src/buildtool/logging/log_level.hpp" #include "src/buildtool/logging/logger.hpp" +// Since it is impossible either forward declare SHA*_CTX (they are typedefs), +// nor their basic classes (they are called differently in OpenSSL and +// BoringSSL), an intermediate struct is forward declared in the header. +using VariantContext = std::variant; +struct Hasher::ShaContext final : VariantContext { + using VariantContext::VariantContext; +}; + namespace { -[[nodiscard]] auto CreateHashImpl(Hasher::HashType type) noexcept - -> std::unique_ptr { +inline constexpr int kOpenSslTrue = 1; + +[[nodiscard]] auto CreateShaContext(Hasher::HashType type) noexcept + -> std::unique_ptr { switch (type) { case Hasher::HashType::SHA1: - return CreateHashImplSha1(); + return std::make_unique(SHA_CTX{}); case Hasher::HashType::SHA256: - return CreateHashImplSha256(); + return std::make_unique(SHA256_CTX{}); case Hasher::HashType::SHA512: - return CreateHashImplSha512(); + return std::make_unique(SHA512_CTX{}); } return nullptr; // make gcc happy } + +template +[[nodiscard]] auto Visit(gsl::not_null const& ctx, + Args&&... visitor_args) noexcept { + try { + return std::visit(TVisitor{std::forward(visitor_args)...}, *ctx); + } catch (std::exception const& e) { + Logger::Log(LogLevel::Error, + "HashFunction::{} failed with an exception:\n{}", + TVisitor::kLogInfo, + e.what()); + Ensures(false); + } +} + +struct InitializeVisitor final { + static constexpr std::string_view kLogInfo = "Initialize"; + + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA_CTX& ctx) const -> bool { + return SHA1_Init(&ctx) == kOpenSslTrue; + } + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA256_CTX& ctx) const -> bool { + return SHA256_Init(&ctx) == kOpenSslTrue; + } + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA512_CTX& ctx) const -> bool { + return SHA512_Init(&ctx) == kOpenSslTrue; + } +}; + +struct UpdateVisitor final { + static constexpr std::string_view kLogInfo = "Update"; + + explicit UpdateVisitor(gsl::not_null const& data) + : data_{*data} {} + + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA_CTX& ctx) const -> bool { + return SHA1_Update(&ctx, data_.data(), data_.size()) == kOpenSslTrue; + } + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA256_CTX& ctx) const -> bool { + return SHA256_Update(&ctx, data_.data(), data_.size()) == kOpenSslTrue; + } + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA512_CTX& ctx) const -> bool { + return SHA512_Update(&ctx, data_.data(), data_.size()) == kOpenSslTrue; + } + + private: + std::string const& data_; +}; + +struct FinalizeVisitor final { + static constexpr std::string_view kLogInfo = "Finalize"; + + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA_CTX& ctx) const + -> std::optional; + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA256_CTX& ctx) const + -> std::optional; + // NOLINTNEXTLINE(google-runtime-references) + [[nodiscard]] inline auto operator()(SHA512_CTX& ctx) const + -> std::optional; +}; + +struct LengthVisitor final { + static constexpr std::string_view kLogInfo = "GetHashLength"; + + [[nodiscard]] constexpr auto operator()(SHA_CTX const& /*unused*/) const + -> std::size_t { + return SHA_DIGEST_LENGTH * kCharsPerNumber; + } + [[nodiscard]] constexpr auto operator()(SHA256_CTX const& /*unused*/) const + -> std::size_t { + return SHA256_DIGEST_LENGTH * kCharsPerNumber; + } + [[nodiscard]] constexpr auto operator()(SHA512_CTX const& /*unused*/) const + -> std::size_t { + return SHA512_DIGEST_LENGTH * kCharsPerNumber; + } + + private: + static constexpr size_t kCharsPerNumber = + std::numeric_limits::max() / + std::numeric_limits::max(); +}; } // namespace -Hasher::Hasher(std::unique_ptr impl) noexcept - : impl_{std::move(impl)} {} +Hasher::Hasher(std::unique_ptr sha_ctx) noexcept + : sha_ctx_{std::move(sha_ctx)} {} + +// Explicitly declared and then defaulted dtor and move ctor/operator are needed +// to compile std::unique_ptr of an incomplete type. +Hasher::Hasher(Hasher&& other) noexcept = default; +auto Hasher::operator=(Hasher&& other) noexcept -> Hasher& = default; +Hasher::~Hasher() noexcept = default; auto Hasher::Create(HashType type) noexcept -> std::optional { - auto impl = CreateHashImpl(type); - if (impl == nullptr) { - return std::nullopt; + auto sha_ctx = CreateShaContext(type); + if (sha_ctx != nullptr and Visit(sha_ctx.get())) { + return std::optional{Hasher{std::move(sha_ctx)}}; } - return Hasher{std::move(impl)}; + return std::nullopt; } auto Hasher::Update(std::string const& data) noexcept -> bool { - return impl_->Update(data); + return Visit(sha_ctx_.get(), &data); } auto Hasher::Finalize() && noexcept -> HashDigest { - auto hash = std::move(*impl_).Finalize(); - if (not hash) { - Logger::Log(LogLevel::Error, "Failed to compute hash."); - std::terminate(); + if (auto hash = Visit(sha_ctx_.get())) { + return HashDigest{std::move(*hash)}; } - return HashDigest{std::move(*hash)}; + Logger::Log(LogLevel::Error, "Failed to compute hash."); + Ensures(false); } auto Hasher::GetHashLength() const noexcept -> std::size_t { - return impl_->GetHashLength(); + return Visit(sha_ctx_.get()); +} + +namespace { +auto FinalizeVisitor::operator()(SHA_CTX& ctx) const + -> std::optional { + auto out = std::array{}; + if (SHA1_Final(out.data(), &ctx) == kOpenSslTrue) { + return std::string{out.begin(), out.end()}; + } + return std::nullopt; +} +auto FinalizeVisitor::operator()(SHA256_CTX& ctx) const + -> std::optional { + auto out = std::array{}; + if (SHA256_Final(out.data(), &ctx) == kOpenSslTrue) { + return std::string{out.begin(), out.end()}; + } + return std::nullopt; } +auto FinalizeVisitor::operator()(SHA512_CTX& ctx) const + -> std::optional { + auto out = std::array{}; + if (SHA512_Final(out.data(), &ctx) == kOpenSslTrue) { + return std::string{out.begin(), out.end()}; + } + return std::nullopt; +} + +} // namespace diff --git a/src/buildtool/crypto/hasher.hpp b/src/buildtool/crypto/hasher.hpp index c37cd34e..4987f7bb 100644 --- a/src/buildtool/crypto/hasher.hpp +++ b/src/buildtool/crypto/hasher.hpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -31,6 +30,8 @@ class Hasher final { /// \brief Types of hash implementations supported by generator. enum class HashType : std::uint8_t { SHA1, SHA256, SHA512 }; + struct ShaContext; + /// \brief The universal hash digest. /// The type of hash and the digest length depends on the hash /// implementation used to generated this digest. @@ -40,11 +41,11 @@ class Hasher final { public: /// \brief Get pointer to raw bytes of digest. /// Length can be obtained using \ref Length. - [[nodiscard]] auto Bytes() const& -> std::string const& { + [[nodiscard]] auto Bytes() const& noexcept -> std::string const& { return bytes_; } - [[nodiscard]] auto Bytes() && -> std::string { + [[nodiscard]] auto Bytes() && noexcept -> std::string { return std::move(bytes_); } @@ -65,35 +66,18 @@ class Hasher final { explicit HashDigest(std::string bytes) : bytes_{std::move(bytes)} {} }; - /// \brief Interface for hash implementations - class IHashImpl { - public: - static constexpr size_t kCharsPerNumber = - std::numeric_limits::max() / - std::numeric_limits::max(); - - IHashImpl() noexcept = default; - IHashImpl(IHashImpl const&) = delete; - IHashImpl(IHashImpl&&) = default; - auto operator=(IHashImpl const&) -> IHashImpl& = delete; - auto operator=(IHashImpl&&) -> IHashImpl& = default; - virtual ~IHashImpl() = default; - - /// \brief Feed data to the incremental hashing. - [[nodiscard]] virtual auto Update(std::string const& data) noexcept - -> bool = 0; - - /// \brief Finalize the hashing and return hash as string of raw bytes. - [[nodiscard]] virtual auto Finalize() && noexcept - -> std::optional = 0; - - /// \brief Obtain length of the resulting hash string. - [[nodiscard]] virtual auto GetHashLength() const noexcept -> size_t = 0; - }; - + /// \brief Create and initialize a hasher + /// \return An initialized hasher on success or std::nullopt on failure. [[nodiscard]] static auto Create(HashType type) noexcept -> std::optional; + Hasher(Hasher&& other) noexcept; + auto operator=(Hasher&& other) noexcept -> Hasher&; + + Hasher(Hasher const& other) noexcept = delete; + auto operator=(Hasher const& other) noexcept -> Hasher& = delete; + ~Hasher() noexcept; + /// \brief Feed data to the hasher. auto Update(std::string const& data) noexcept -> bool; @@ -104,9 +88,9 @@ class Hasher final { [[nodiscard]] auto GetHashLength() const noexcept -> std::size_t; private: - std::unique_ptr impl_; + std::unique_ptr sha_ctx_; - explicit Hasher(std::unique_ptr impl) noexcept; + explicit Hasher(std::unique_ptr sha_ctx) noexcept; }; #endif // INCLUDED_SRC_BUILDTOOL_CRYPTO_HASHER_HPP -- cgit v1.2.3