Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ set(opensslpp_source_files
src/opensslpp/crypto_rng.hpp
src/opensslpp/crypto_rng.cpp

src/opensslpp/digest_context_fwd.hpp
src/opensslpp/digest_context.hpp
src/opensslpp/digest_context.cpp

src/opensslpp/core_error_fwd.hpp
src/opensslpp/core_error.hpp
src/opensslpp/core_error.cpp
Expand Down
108 changes: 7 additions & 101 deletions src/minimysql/caching_sha2_password_authenticator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,108 +17,13 @@

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <stdexcept>
#include <iterator>
#include <string>
#include <string_view>

#include <openssl/evp.h>
#include <openssl/types.h>

namespace {

enum class digest_code_type : std::uint8_t {
sha256,
};

class digest_context {
public:
// no std::string_view for 'type' as we need it to be nul-terminated
explicit digest_context(digest_code_type digest_code)
: impl_{EVP_MD_CTX_new(), digest_context_deleter{}} {
if (!impl_) {
throw std::runtime_error{"failed to create digest context"};
}
if (EVP_DigestInit_ex(impl_.get(), get_md_by_digest_code(digest_code),
nullptr) == 0) {
throw std::runtime_error{"failed to initialize digest context"};
}
}

~digest_context() noexcept = default;

digest_context(const digest_context &obj) = delete;
digest_context(digest_context &&obj) noexcept = delete;

digest_context &operator=(const digest_context &obj) = delete;
digest_context &operator=(digest_context &&obj) noexcept = delete;

[[nodiscard]] std::size_t get_size_in_bytes() const noexcept {
assert(impl_);
auto native_result{EVP_MD_CTX_size(impl_.get())};
assert(native_result != -1);
return static_cast<std::size_t>(native_result);
}

void update(std::string_view data) {
assert(impl_);
if (EVP_DigestUpdate(impl_.get(), std::data(data), std::size(data)) == 0) {
throw std::runtime_error{"failed to update digest context"};
}
}
std::string finalize() {
assert(impl_);
std::string result(get_size_in_bytes(), '\0');

unsigned int result_size = 0;
if (EVP_DigestFinal_ex(
impl_.get(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<unsigned char *>(std::data(result)),
&result_size) == 0) {
throw std::runtime_error{"cannot finalize digest context"};
}
assert(result_size == std::size(result));

impl_.reset();
return result;
}

private:
struct digest_context_deleter {
void operator()(EVP_MD_CTX *digest_context) const noexcept {
// null-ness is handled by EVP_MD_CTX_free
EVP_MD_CTX_free(digest_context);
}
};

using impl_ptr = std::unique_ptr<EVP_MD_CTX, digest_context_deleter>;
impl_ptr impl_;

[[nodiscard]] static const EVP_MD *
get_md_by_digest_code(digest_code_type digest_code) noexcept {
switch (digest_code) {
case digest_code_type::sha256:
return EVP_sha256();
default:
// should never happen as we only construct digest_context with supported
// digest_code_type
return nullptr;
}
}
};

std::string calculate_digest(digest_code_type digest_code,
std::string_view data) {
digest_context ctx(digest_code);
ctx.update(data);
return ctx.finalize();
}

} // anonymous namespace
#include "opensslpp/digest_context.hpp"

namespace minimysql {

Expand All @@ -132,16 +37,17 @@ std::string caching_sha2_password_authenticator::scramble(
// server, provided that it knows original password and server_auth_data
// (salt), can verify client_auth_data by calculating the same way and
// comparing the result with client_auth_data
const auto digest_code{digest_code_type::sha256};
const std::string digest_name{"SHA256"};

// calculating hashed password
auto result{calculate_digest(digest_code, password)};
auto result{opensslpp::digest_context::calculate(digest_name, password)};

// calculating double-hashed password
const auto double_hashed_password{calculate_digest(digest_code, result)};
const auto double_hashed_password{
opensslpp::digest_context::calculate(digest_name, result)};

// calculating salted triple-hashed password
digest_context ctx(digest_code);
opensslpp::digest_context ctx{digest_name};
ctx.update(double_hashed_password);
ctx.update(salt);
const auto salted_triple_hashed_password{ctx.finalize()};
Expand Down
97 changes: 97 additions & 0 deletions src/opensslpp/digest_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) 2023-2026 Percona and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include "opensslpp/digest_context.hpp"

#include <cassert>
#include <cstddef>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include <openssl/evp.h>
#include <openssl/types.h>

#include "opensslpp/core_error.hpp"

#include "util/exception_location_helpers.hpp"

namespace opensslpp {

void digest_context::impl_deleter::operator()(void *digest_ctx) const noexcept {
if (digest_ctx != nullptr) {
EVP_MD_CTX_free(static_cast<EVP_MD_CTX *>(digest_ctx));
}
}

digest_context::digest_context(const std::string &digest_name) {
const auto *digest{EVP_get_digestbyname(digest_name.c_str())};
if (digest == nullptr) {
util::exception_location().raise<core_error>("unknown digest name");
}

std::unique_ptr<void, impl_deleter> new_impl{EVP_MD_CTX_new(),
impl_deleter{}};
if (!new_impl) {
util::exception_location().raise<core_error>(
"cannot allocate digest context");
}
if (EVP_DigestInit_ex(static_cast<EVP_MD_CTX *>(new_impl.get()), digest,
nullptr) == 0) {
util::exception_location().raise<core_error>(
"cannot initialize digest context");
}
impl_ = std::move(new_impl);
}

void digest_context::update(std::string_view data) {
assert(impl_);
if (std::empty(data)) {
return;
}
if (EVP_DigestUpdate(static_cast<EVP_MD_CTX *>(impl_.get()), std::data(data),
std::size(data)) == 0) {
util::exception_location().raise<core_error>(
"cannot update digest context");
}
}

std::string digest_context::finalize() {
assert(impl_);
auto *ctx{static_cast<EVP_MD_CTX *>(impl_.get())};
std::string result(static_cast<std::size_t>(EVP_MD_CTX_size(ctx)), '\0');
unsigned int size{0U};
if (EVP_DigestFinal_ex(
ctx,
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<unsigned char *>(std::data(result)), &size) == 0) {
util::exception_location().raise<core_error>(
"cannot finalize digest context");
}
assert(static_cast<std::size_t>(size) == std::size(result));
impl_.reset();
return result;
}

std::string digest_context::calculate(const std::string &digest_name,
std::string_view data) {
digest_context ctx{digest_name};
ctx.update(data);
return ctx.finalize();
}

} // namespace opensslpp
57 changes: 57 additions & 0 deletions src/opensslpp/digest_context.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2023-2026 Percona and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#ifndef OPENSSLPP_DIGEST_CONTEXT_HPP
#define OPENSSLPP_DIGEST_CONTEXT_HPP

#include "opensslpp/digest_context_fwd.hpp" // IWYU pragma: export

#include <memory>
#include <string>
#include <string_view>

namespace opensslpp {

class digest_context {
public:
digest_context() noexcept = default;
// 'digest_name' must be a valid digest name supported by OpenSSL
// (e.g. "SHA256")
explicit digest_context(const std::string &digest_name);
~digest_context() noexcept = default;

digest_context(const digest_context &) = delete;
digest_context(digest_context &&) noexcept = default;
digest_context &operator=(const digest_context &) = delete;
digest_context &operator=(digest_context &&) noexcept = default;

[[nodiscard]] bool is_empty() const noexcept { return !impl_; }

void update(std::string_view data);
[[nodiscard]] std::string finalize();

[[nodiscard]] static std::string calculate(const std::string &digest_name,
std::string_view data);

private:
struct impl_deleter {
void operator()(void *digest_ctx) const noexcept;
};
std::unique_ptr<void, impl_deleter> impl_;
};

} // namespace opensslpp

#endif // OPENSSLPP_DIGEST_CONTEXT_HPP
25 changes: 25 additions & 0 deletions src/opensslpp/digest_context_fwd.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2023-2026 Percona and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#ifndef OPENSSLPP_DIGEST_CONTEXT_FWD_HPP
#define OPENSSLPP_DIGEST_CONTEXT_FWD_HPP

namespace opensslpp {

class digest_context;

} // namespace opensslpp

#endif // OPENSSLPP_DIGEST_CONTEXT_FWD_HPP
14 changes: 14 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ set_target_properties(crypto_rnd_test PROPERTIES
CXX_EXTENSIONS NO
)

add_executable(digest_context_test digest_context_test.cpp)
target_include_directories(digest_context_test PRIVATE "${PROJECT_SOURCE_DIR}/src")
target_link_libraries(digest_context_test
PRIVATE
binlog_server_compiler_flags
binsrv::lib_opensslpp
Boost::unit_test_framework
)
set_target_properties(digest_context_test PROPERTIES
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)


set(test_run_options --no_color_output)
add_test(NAME byte_span_encoding_test COMMAND byte_span_encoding_test ${test_run_options})
Expand All @@ -131,3 +144,4 @@ add_test(NAME gtid_set_test COMMAND gtid_set_test ${test_run_options})
add_test(NAME event_test COMMAND event_test ${test_run_options})
add_test(NAME cipher_context_test COMMAND cipher_context_test ${test_run_options})
add_test(NAME crypto_rnd_test COMMAND crypto_rnd_test ${test_run_options})
add_test(NAME digest_context_test COMMAND digest_context_test ${test_run_options})
Loading
Loading