From 9bf70a31e3e076909e0ce347e6eac3aec033ef46 Mon Sep 17 00:00:00 2001 From: ruben Date: Tue, 7 Apr 2020 11:37:40 +0100 Subject: [PATCH] Refactored auth calculation --- TODO.txt | 2 - include/boost/mysql/detail/auth/auth.hpp | 119 ------------ .../mysql/detail/auth/auth_calculator.hpp | 53 ++++++ .../detail/auth/caching_sha2_password.hpp | 33 ++-- .../detail/auth/impl/auth_calculator.ipp | 74 ++++++++ ...password.hpp => caching_sha2_password.ipp} | 62 ++++++- .../auth/impl/mysql_native_password.ipp | 40 +++- .../detail/auth/mysql_native_password.hpp | 17 +- .../network_algorithms/impl/handshake.hpp | 33 ++-- .../boost/mysql/detail/protocol/constants.hpp | 1 + test/CMakeLists.txt | 3 +- test/unit/detail/auth/auth_calculator.cpp | 174 ++++++++++++++++++ .../detail/auth/caching_sha2_password.cpp | 27 --- .../detail/auth/mysql_native_password.cpp | 37 ---- .../detail/protocol/handshake_messages.cpp | 8 + 15 files changed, 460 insertions(+), 223 deletions(-) delete mode 100644 include/boost/mysql/detail/auth/auth.hpp create mode 100644 include/boost/mysql/detail/auth/auth_calculator.hpp create mode 100644 include/boost/mysql/detail/auth/impl/auth_calculator.ipp rename include/boost/mysql/detail/auth/impl/{caching_sha2_password.hpp => caching_sha2_password.ipp} (50%) create mode 100644 test/unit/detail/auth/auth_calculator.cpp delete mode 100644 test/unit/detail/auth/caching_sha2_password.cpp delete mode 100644 test/unit/detail/auth/mysql_native_password.cpp diff --git a/TODO.txt b/TODO.txt index 18dc3305..25e2599d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,11 +1,9 @@ sha256_password auth plugin - Unit tests for auth calculator & refactor Set error_info when there is an auth plugin involved Unknown auth plugin Auth plugin requires SSL Test for an unknown auth plugin Update readme - Unit test for auth_more_data Multiresultset Text protocol Binary protocol (stored procedures) diff --git a/include/boost/mysql/detail/auth/auth.hpp b/include/boost/mysql/detail/auth/auth.hpp deleted file mode 100644 index 01fe11d8..00000000 --- a/include/boost/mysql/detail/auth/auth.hpp +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_HPP_ -#define INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_HPP_ - -#include "boost/mysql/detail/auth/mysql_native_password.hpp" -#include "boost/mysql/detail/auth/caching_sha2_password.hpp" -#include "boost/mysql/error.hpp" - -namespace boost { -namespace mysql { -namespace detail { - -class auth_response_calculator -{ - //std::array auth_response_buffer_ {}; - //std::string_view res_; - //bool use_buffer_ {}; - std::string response_; - std::string plugin_name_; - - error_code calculate_impl( - std::string_view plugin_name, - std::string_view password, - std::string_view challenge, - bool use_ssl - ) - { - plugin_name_ = plugin_name; - - // Blank password: we should just return an empty auth string - if (password.empty()) - { - response_ = ""; - return error_code(); - } - - if (plugin_name == mysql_native_password::plugin_name) - { - // Check challenge size - if (challenge.size() != mysql_native_password::challenge_length) - { - return make_error_code(errc::protocol_value_error); - } - - // Do the calculation - std::array buff; - mysql_native_password::compute_auth_string( - password, - challenge.data(), - buff.data() - ); - response_.assign(reinterpret_cast(buff.data()), buff.size()); - return error_code(); - } - else if (plugin_name == "caching_sha2_password") - { - if (challenge.size() == 1 && challenge[0] == 4) - { - if (!use_ssl) - { - return make_error_code(errc::auth_plugin_requires_ssl); - } - response_ = password; - response_.push_back(0); - return error_code(); - } - else - { - // Check challenge size - if (challenge.size() != caching_sha2_password::challenge_length) - { - return make_error_code(errc::protocol_value_error); - } - - // Do the calculation - std::array buff; - caching_sha2_password::compute_auth_string( - password, - challenge.data(), - buff.data() - ); - response_.assign(reinterpret_cast(buff.data()), buff.size()); - return error_code(); - } - } - else - { - return make_error_code(errc::unknown_auth_plugin); - } - } -public: - error_code calculate( - std::string_view plugin_name, - std::string_view password, - std::string_view challenge, - bool use_ssl - ) - { - auto res = calculate_impl(plugin_name, password, challenge, use_ssl); - if (res) - { - response_.clear(); - } - return res; - } - - std::string_view response() const noexcept - { - return response_; - } - const auto& get_plugin_name() const { return plugin_name_; } -}; - -} -} -} - - - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_HPP_ */ diff --git a/include/boost/mysql/detail/auth/auth_calculator.hpp b/include/boost/mysql/detail/auth/auth_calculator.hpp new file mode 100644 index 00000000..175bf490 --- /dev/null +++ b/include/boost/mysql/detail/auth/auth_calculator.hpp @@ -0,0 +1,53 @@ +#ifndef INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_CALCULATOR_HPP_ +#define INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_CALCULATOR_HPP_ + +#include "boost/mysql/error.hpp" +#include +#include +#include + +namespace boost { +namespace mysql { +namespace detail { + +struct authentication_plugin +{ + using calculator_signature = error_code (*)( + std::string_view password, + std::string_view challenge, + bool use_ssl, + std::string& output + ); + + std::string_view name; + calculator_signature calculator; +}; + +class auth_calculator +{ + const authentication_plugin* plugin_ {nullptr}; + std::string response_; + + inline static const authentication_plugin* find_plugin(std::string_view name); +public: + inline error_code calculate( + std::string_view plugin_name, + std::string_view password, + std::string_view challenge, + bool use_ssl + ); + std::string_view response() const noexcept { return response_; } + std::string_view plugin_name() const noexcept + { + assert(plugin_); + return plugin_->name; + } +}; + +} // detail +} // mysql +} // boost + +#include "boost/mysql/detail/auth/impl/auth_calculator.ipp" + +#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_AUTH_CALCULATOR_HPP_ */ diff --git a/include/boost/mysql/detail/auth/caching_sha2_password.hpp b/include/boost/mysql/detail/auth/caching_sha2_password.hpp index 92524595..700f8183 100644 --- a/include/boost/mysql/detail/auth/caching_sha2_password.hpp +++ b/include/boost/mysql/detail/auth/caching_sha2_password.hpp @@ -3,25 +3,34 @@ #include #include +#include "boost/mysql/error.hpp" namespace boost { namespace mysql { namespace detail { namespace caching_sha2_password { -constexpr const char* plugin_name = "caching_sha2_password"; -constexpr std::size_t challenge_length = 20; -constexpr std::size_t response_length = 32; +// Authorization for this plugin may be cleartext password or challenge/response. +// The server has a cache that uses when employing challenge/response. When +// the server sends a challenge of challenge_length bytes, we should send +// the password hashed with the challenge. The server may send a challenge +// equals to perform_full_auth, meaning it could not use the cache to +// complete the auth. In this case, we should just send the cleartext password. +// Doing the latter requires a SSL connection. It is possible to perform full +// auth without an SSL connection, but that requires the server public key, +// and we do not implement that. +inline error_code compute_response( + std::string_view password, + std::string_view challenge, + bool use_ssl, + std::string& output +); -// challenge must point to challenge_length bytes of data -// output must point to response_length bytes of data -inline void compute_auth_string(std::string_view password, const void* challenge, void* output); +} // caching_sha2_password +} // detail +} // mysql +} // boost -} -} -} -} - -#include "boost/mysql/detail/auth/impl/caching_sha2_password.hpp" +#include "boost/mysql/detail/auth/impl/caching_sha2_password.ipp" #endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_CACHING_SHA2_PASSWORD_HPP_ */ diff --git a/include/boost/mysql/detail/auth/impl/auth_calculator.ipp b/include/boost/mysql/detail/auth/impl/auth_calculator.ipp new file mode 100644 index 00000000..0162ac95 --- /dev/null +++ b/include/boost/mysql/detail/auth/impl/auth_calculator.ipp @@ -0,0 +1,74 @@ +#ifndef INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_AUTH_CALCULATOR_IPP_ +#define INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_AUTH_CALCULATOR_IPP_ + +#include "boost/mysql/detail/auth/mysql_native_password.hpp" +#include "boost/mysql/detail/auth/caching_sha2_password.hpp" + +namespace boost { +namespace mysql { +namespace detail { + +constexpr authentication_plugin mysql_native_password_plugin { + "mysql_native_password", + &mysql_native_password::compute_response +}; + +constexpr authentication_plugin caching_sha2_password_plugin { + "caching_sha2_password", + &caching_sha2_password::compute_response +}; + +constexpr std::array all_authentication_plugins { + &mysql_native_password_plugin, + &caching_sha2_password_plugin +}; + +} // detail +} // mysql +} // boost + +inline const boost::mysql::detail::authentication_plugin* +boost::mysql::detail::auth_calculator::find_plugin( + std::string_view name +) +{ + auto it = std::find_if( + all_authentication_plugins.begin(), + all_authentication_plugins.end(), + [name](const authentication_plugin* plugin) { return plugin->name == name; } + ); + return it == std::end(all_authentication_plugins) ? nullptr : *it; +} + +inline boost::mysql::error_code +boost::mysql::detail::auth_calculator::calculate( + std::string_view plugin_name, + std::string_view password, + std::string_view challenge, + bool use_ssl +) +{ + + plugin_ = find_plugin(plugin_name); + if (plugin_) + { + // Blank password: we should just return an empty auth string + if (password.empty()) + { + response_ = ""; + return error_code(); + } + else + { + return plugin_->calculator(password, challenge, use_ssl, response_); + } + } + else + { + return make_error_code(errc::unknown_auth_plugin); + } +} + + + +#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_AUTH_CALCULATOR_IPP_ */ diff --git a/include/boost/mysql/detail/auth/impl/caching_sha2_password.hpp b/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp similarity index 50% rename from include/boost/mysql/detail/auth/impl/caching_sha2_password.hpp rename to include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp index aacd53c6..b2f2a9f0 100644 --- a/include/boost/mysql/detail/auth/impl/caching_sha2_password.hpp +++ b/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp @@ -1,10 +1,21 @@ -#ifndef INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_HPP_ -#define INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_HPP_ +#ifndef INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_IPP_ +#define INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_IPP_ #include #include -inline void boost::mysql::detail::caching_sha2_password::compute_auth_string( +namespace boost { +namespace mysql { +namespace detail { +namespace caching_sha2_password { + +constexpr std::size_t challenge_length = 20; +constexpr std::size_t response_length = 32; +constexpr std::string_view perform_full_auth = "\4"; + +// challenge must point to challenge_length bytes of data +// output must point to response_length bytes of data +inline void compute_auth_string( std::string_view password, const void* challenge, void* output @@ -34,6 +45,49 @@ inline void boost::mysql::detail::caching_sha2_password::compute_auth_string( } } +} // caching_sha2_password +} // detail +} // mysql +} // boost -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_HPP_ */ + +inline boost::mysql::error_code +boost::mysql::detail::caching_sha2_password::compute_response( + std::string_view password, + std::string_view challenge, + bool use_ssl, + std::string& output +) +{ + if (challenge == perform_full_auth) + { + if (!use_ssl) + { + return make_error_code(errc::auth_plugin_requires_ssl); + } + output = password; + output.push_back(0); + return error_code(); + } + else + { + // Check challenge size + if (challenge.size() != challenge_length) + { + return make_error_code(errc::protocol_value_error); + } + + // Do the calculation + output.resize(response_length); + compute_auth_string( + password, + challenge.data(), + output.data() + ); + return error_code(); + } +} + + +#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUTH_IMPL_CACHING_SHA2_PASSWORD_IPP_ */ diff --git a/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp b/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp index a716c958..acd9fd3f 100644 --- a/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp +++ b/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp @@ -4,8 +4,18 @@ #include #include +namespace boost { +namespace mysql { +namespace detail { +namespace mysql_native_password { + +constexpr std::size_t challenge_length = 20; +constexpr std::size_t response_length = 20; + +// challenge must point to challenge_length bytes of data +// output must point to response_length bytes of data // SHA1( password ) XOR SHA1( "20-bytes random data from server" SHA1( SHA1( password ) ) ) -inline void boost::mysql::detail::mysql_native_password::compute_auth_string( +inline void compute_auth_string( std::string_view password, const void* challenge, void* output @@ -31,8 +41,36 @@ inline void boost::mysql::detail::mysql_native_password::compute_auth_string( } } +} // mysql_native_password +} // detail +} // mysql +} // boost +inline boost::mysql::error_code +boost::mysql::detail::mysql_native_password::compute_response( + std::string_view password, + std::string_view challenge, + bool, // use_ssl + std::string& output +) +{ + // Check challenge size + if (challenge.size() != challenge_length) + { + return make_error_code(errc::protocol_value_error); + } + + // Do the calculation + output.resize(response_length); + compute_auth_string( + password, + challenge.data(), + output.data() + ); + return error_code(); +} + #endif diff --git a/include/boost/mysql/detail/auth/mysql_native_password.hpp b/include/boost/mysql/detail/auth/mysql_native_password.hpp index 92b488b2..54b79c27 100644 --- a/include/boost/mysql/detail/auth/mysql_native_password.hpp +++ b/include/boost/mysql/detail/auth/mysql_native_password.hpp @@ -3,20 +3,21 @@ #include #include -#include +#include "boost/mysql/error.hpp" namespace boost { namespace mysql { namespace detail { namespace mysql_native_password { -constexpr const char* plugin_name = "mysql_native_password"; -constexpr std::size_t challenge_length = 20; -constexpr std::size_t response_length = 20; - -// challenge must point to challenge_length bytes of data -// output must point to response_length bytes of data -inline void compute_auth_string(std::string_view password, const void* challenge, void* output); +// Authorization for this plugin is always challenge (nonce) -> response +// (hashed password). +inline error_code compute_response( + std::string_view password, + std::string_view challenge, + bool use_ssl, + std::string& output +); } // mysql_native_password diff --git a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp index bcbd41bc..0ed72687 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp @@ -4,7 +4,7 @@ #include "boost/mysql/detail/network_algorithms/common.hpp" #include "boost/mysql/detail/protocol/capabilities.hpp" #include "boost/mysql/detail/protocol/handshake_messages.hpp" -#include "boost/mysql/detail/auth/auth.hpp" +#include "boost/mysql/detail/auth/auth_calculator.hpp" #include namespace boost { @@ -45,6 +45,16 @@ inline error_code deserialize_handshake( return deserialize_message(output, ctx); } +// When receiving an auth response from the server, several things can happen: +// - An OK packet. It means we are done with the auth phase. auth_result::complete. +// - An auth switch response. It means we should change the auth plugin, +// recalculate the auth response and send it back. auth_result::send_more_data. +// - An auth more data. Same as auth switch response, but without changing +// the authentication plugin. Also auth_result::send_more_data. +// - An auth more data with a challenge equals to fast_auth_complete_challenge. +// This means auth is complete and we should wait for an OK packet (auth_result::wait_for_ok). +// I have no clue why the server sends this instead of just an OK packet. It +// happens just for caching_sha2_password. enum class auth_result { complete, @@ -57,7 +67,7 @@ class handshake_processor { connection_params params_; capabilities negotiated_caps_; - auth_response_calculator auth_calc_; + auth_calculator auth_calc_; public: handshake_processor(const connection_params& params): params_(params) {}; capabilities negotiated_capabilities() const noexcept { return negotiated_caps_; } @@ -125,7 +135,7 @@ public: string_null(params_.username()), string_lenenc(auth_calc_.response()), string_null(params_.database()), - string_null(auth_calc_.get_plugin_name()) + string_null(auth_calc_.plugin_name()) }; // Serialize @@ -183,26 +193,27 @@ public: err = deserialize_message(more_data, ctx); if (err) return err; - // TODO: refactor this - std::string_view data = more_data.auth_plugin_data.value; - if (data.size() == 1 && data[0] == 3) + std::string_view challenge = more_data.auth_plugin_data.value; + if (challenge == fast_auth_complete_challenge) { result = auth_result::wait_for_ok; return error_code(); } // Compute response - auth_switch_response_packet auth_sw_res; err = auth_calc_.calculate( - auth_calc_.get_plugin_name(), + auth_calc_.plugin_name(), params_.password(), - more_data.auth_plugin_data.value, + challenge, use_ssl() ); if (err) return err; - auth_sw_res.auth_plugin_data.value = auth_calc_.response(); - serialize_message(auth_sw_res, negotiated_caps_, buffer); + serialize_message( + auth_switch_response_packet {string_eof(auth_calc_.response())}, + negotiated_caps_, + buffer + ); result = auth_result::send_more_data; return error_code(); diff --git a/include/boost/mysql/detail/protocol/constants.hpp b/include/boost/mysql/detail/protocol/constants.hpp index 0f4c561a..a6802fdd 100644 --- a/include/boost/mysql/detail/protocol/constants.hpp +++ b/include/boost/mysql/detail/protocol/constants.hpp @@ -64,6 +64,7 @@ constexpr std::uint8_t ok_packet_header = 0x00; constexpr std::uint8_t eof_packet_header = 0xfe; constexpr std::uint8_t auth_switch_request_header = 0xfe; constexpr std::uint8_t auth_more_data_header = 0x01; +constexpr std::string_view fast_auth_complete_challenge = "\3"; // Column flags namespace column_flags diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 007103bf..4681ef2f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,8 +17,7 @@ endif() # of the runtime penalty (specially considerable in integration tests) add_executable( mysql_unittests - unit/detail/auth/mysql_native_password.cpp - unit/detail/auth/caching_sha2_password.cpp + unit/detail/auth/auth_calculator.cpp unit/detail/protocol/serialization_test_common.cpp unit/detail/protocol/serialization.cpp unit/detail/protocol/common_messages.cpp diff --git a/test/unit/detail/auth/auth_calculator.cpp b/test/unit/detail/auth/auth_calculator.cpp new file mode 100644 index 00000000..03ed7627 --- /dev/null +++ b/test/unit/detail/auth/auth_calculator.cpp @@ -0,0 +1,174 @@ + +#include "boost/mysql/detail/auth/auth_calculator.hpp" +#include "test_common.hpp" +#include + +using namespace boost::mysql::detail; +using namespace boost::mysql::test; +using namespace testing; +using boost::mysql::error_code; +using boost::mysql::errc; + +namespace +{ + +// mysql_native_password +struct MysqlNativePasswordTest : public Test +{ + auth_calculator calc; + std::uint8_t challenge_buffer [20] { + 0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47, + 0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03, + 0x3d, 0x27, 0x3a, 0x4c + }; // Values snooped using Wireshark + std::uint8_t expected_buffer [20] { + 0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8, + 0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1, + 0x10, 0x50, 0xd4, 0x28 + }; + std::string_view challenge = makesv(challenge_buffer); + std::string_view expected = makesv(expected_buffer); +}; + +TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslFalse_ReturnsExpectedHash) +{ + auto err = calc.calculate("mysql_native_password", "root", challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), expected); + EXPECT_EQ(calc.plugin_name(), "mysql_native_password"); +} + +TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslTrue_ReturnsExpectedHash) +{ + auto err = calc.calculate("mysql_native_password", "root", challenge, true); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), expected); + EXPECT_EQ(calc.plugin_name(), "mysql_native_password"); +} + +TEST_F(MysqlNativePasswordTest, EmptyPasswordSslFalse_ReturnsEmpty) +{ + auto err = calc.calculate("mysql_native_password", "", challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "mysql_native_password"); +} + +TEST_F(MysqlNativePasswordTest, EmptyPasswordSslTrue_ReturnsEmpty) +{ + auto err = calc.calculate("mysql_native_password", "", challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "mysql_native_password"); +} + +TEST_F(MysqlNativePasswordTest, BadChallengeLength_Fail) +{ + EXPECT_EQ((calc.calculate("mysql_native_password", "password", "", true)), + make_error_code(errc::protocol_value_error)); + EXPECT_EQ((calc.calculate("mysql_native_password", "password", "bad_challenge", true)), + make_error_code(errc::protocol_value_error)); +} + +// caching_sha2_password +struct CachingSha2PasswordTest : public Test +{ + auth_calculator calc; + std::uint8_t challenge_buffer [20] { + 0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a, + 0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22, + 0x46, 0x8, 0x18, 0x1 + }; // Values snooped using the MySQL Python connector + std::uint8_t expected_buffer [32] { + 0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b, + 0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e, + 0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6, + 0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20 + }; + std::string_view challenge = makesv(challenge_buffer); + std::string_view expected = makesv(expected_buffer); + std::string_view cleartext_challenge { "\4" }; +}; + +TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslFalse_ReturnsExpectedHash) +{ + auto err = calc.calculate("caching_sha2_password", "hola", challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), expected); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslTrue_ReturnsExpectedHash) +{ + auto err = calc.calculate("caching_sha2_password", "hola", challenge, true); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), expected); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslFalse_Fail) +{ + auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, false); + EXPECT_EQ(err, make_error_code(errc::auth_plugin_requires_ssl)); +} + +TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslTrue_ReturnsPassword) +{ + auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, true); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), std::string("hola") + '\0'); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslFalse_ReturnsEmpty) +{ + auto err = calc.calculate("caching_sha2_password", "", challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslTrue_ReturnsEmpty) +{ + auto err = calc.calculate("caching_sha2_password", "", challenge, true); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslFalse_ReturnsEmpty) +{ + auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, false); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslTrue_ReturnsEmpty) +{ + auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, true); + EXPECT_EQ(err, error_code()); + EXPECT_EQ(calc.response(), ""); + EXPECT_EQ(calc.plugin_name(), "caching_sha2_password"); +} + +TEST_F(CachingSha2PasswordTest, BadChallengeLength_Fail) +{ + EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "", true)), + make_error_code(errc::protocol_value_error)); + EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "bad_challenge", true)), + make_error_code(errc::protocol_value_error)); +} + +// Bad authentication plugin +TEST(AuthCalculator, UnknownAuthPlugin_Fail) +{ + auth_calculator calc; + EXPECT_EQ((calc.calculate("bad_plugin", "password", "challenge", true)), + make_error_code(errc::unknown_auth_plugin)); + EXPECT_EQ((calc.calculate("", "password", "challenge", true)), + make_error_code(errc::unknown_auth_plugin)); +} + + +} diff --git a/test/unit/detail/auth/caching_sha2_password.cpp b/test/unit/detail/auth/caching_sha2_password.cpp deleted file mode 100644 index 7878ff61..00000000 --- a/test/unit/detail/auth/caching_sha2_password.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "boost/mysql/detail/auth/caching_sha2_password.hpp" -#include -#include - -using namespace boost::mysql::detail; - -TEST(CachingSha2Password, ComputeAuthString_NonEmptyPassword_ReturnsExpectedHash) -{ - // Values snooped using the MySQL Python connector - std::array challenge { - 0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a, - 0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22, - 0x46, 0x8, 0x18, 0x1 - }; - std::array expected { - 0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b, - 0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e, - 0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6, - 0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20 - }; - std::array actual {}; - const char* password = "hola"; - caching_sha2_password::compute_auth_string(password, challenge.data(), actual.data()); - EXPECT_EQ(expected, actual); -} - diff --git a/test/unit/detail/auth/mysql_native_password.cpp b/test/unit/detail/auth/mysql_native_password.cpp deleted file mode 100644 index eb8943b0..00000000 --- a/test/unit/detail/auth/mysql_native_password.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * auth.cpp - * - * Created on: Oct 21, 2019 - * Author: ruben - */ - -#include "boost/mysql/detail/auth/mysql_native_password.hpp" -#include -#include - -using namespace boost::mysql::detail; -using namespace testing; - -namespace -{ - -TEST(MysqlNativePassword, ComputeAuthString_NonEmptyPassword_ReturnsExpectedHash) -{ - // Values snooped using Wireshark - std::array challenge { - 0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47, - 0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03, - 0x3d, 0x27, 0x3a, 0x4c - }; - std::array expected { - 0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8, - 0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1, - 0x10, 0x50, 0xd4, 0x28 - }; - std::array actual {}; - const char* password = "root"; - mysql_native_password::compute_auth_string(password, challenge.data(), actual.data()); - EXPECT_EQ(expected, actual); -} - -} // anon namespace diff --git a/test/unit/detail/protocol/handshake_messages.cpp b/test/unit/detail/protocol/handshake_messages.cpp index bc31d7b0..7eb9cf38 100644 --- a/test/unit/detail/protocol/handshake_messages.cpp +++ b/test/unit/detail/protocol/handshake_messages.cpp @@ -205,5 +205,13 @@ INSTANTIATE_TEST_SUITE_P(SslRequest, SerializeTest, ::testing::Values( }, "default") ), test_name_generator); +INSTANTIATE_TEST_SUITE_P(AuthMoreData, DeserializeTest, ::testing::Values( + serialization_testcase(auth_more_data_packet{ + string_eof("abc") + }, { + 0x61, 0x62, 0x63 + }, "default") +), test_name_generator); + } // anon namespace