summaryrefslogtreecommitdiff
path: root/src/buildtool/crypto/hasher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildtool/crypto/hasher.cpp')
-rw-r--r--src/buildtool/crypto/hasher.cpp179
1 files changed, 158 insertions, 21 deletions
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 <exception>
+#include <limits>
+#include <string_view>
+#include <variant>
+
+#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<SHA_CTX, SHA256_CTX, SHA512_CTX>;
+struct Hasher::ShaContext final : VariantContext {
+ using VariantContext::VariantContext;
+};
+
namespace {
-[[nodiscard]] auto CreateHashImpl(Hasher::HashType type) noexcept
- -> std::unique_ptr<Hasher::IHashImpl> {
+inline constexpr int kOpenSslTrue = 1;
+
+[[nodiscard]] auto CreateShaContext(Hasher::HashType type) noexcept
+ -> std::unique_ptr<Hasher::ShaContext> {
switch (type) {
case Hasher::HashType::SHA1:
- return CreateHashImplSha1();
+ return std::make_unique<Hasher::ShaContext>(SHA_CTX{});
case Hasher::HashType::SHA256:
- return CreateHashImplSha256();
+ return std::make_unique<Hasher::ShaContext>(SHA256_CTX{});
case Hasher::HashType::SHA512:
- return CreateHashImplSha512();
+ return std::make_unique<Hasher::ShaContext>(SHA512_CTX{});
}
return nullptr; // make gcc happy
}
+
+template <typename TVisitor, typename... Args>
+[[nodiscard]] auto Visit(gsl::not_null<VariantContext*> const& ctx,
+ Args&&... visitor_args) noexcept {
+ try {
+ return std::visit(TVisitor{std::forward<Args>(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<std::string const*> 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<std::string>;
+ // NOLINTNEXTLINE(google-runtime-references)
+ [[nodiscard]] inline auto operator()(SHA256_CTX& ctx) const
+ -> std::optional<std::string>;
+ // NOLINTNEXTLINE(google-runtime-references)
+ [[nodiscard]] inline auto operator()(SHA512_CTX& ctx) const
+ -> std::optional<std::string>;
+};
+
+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<std::uint8_t>::max() /
+ std::numeric_limits<signed char>::max();
+};
} // namespace
-Hasher::Hasher(std::unique_ptr<IHashImpl> impl) noexcept
- : impl_{std::move(impl)} {}
+Hasher::Hasher(std::unique_ptr<ShaContext> 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<Hasher> {
- auto impl = CreateHashImpl(type);
- if (impl == nullptr) {
- return std::nullopt;
+ auto sha_ctx = CreateShaContext(type);
+ if (sha_ctx != nullptr and Visit<InitializeVisitor>(sha_ctx.get())) {
+ return std::optional<Hasher>{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<UpdateVisitor>(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<FinalizeVisitor>(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<LengthVisitor>(sha_ctx_.get());
+}
+
+namespace {
+auto FinalizeVisitor::operator()(SHA_CTX& ctx) const
+ -> std::optional<std::string> {
+ auto out = std::array<std::uint8_t, SHA_DIGEST_LENGTH>{};
+ 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<std::string> {
+ auto out = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>{};
+ 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<std::string> {
+ auto out = std::array<std::uint8_t, SHA512_DIGEST_LENGTH>{};
+ if (SHA512_Final(out.data(), &ctx) == kOpenSslTrue) {
+ return std::string{out.begin(), out.end()};
+ }
+ return std::nullopt;
+}
+
+} // namespace