From 4ffdb0721fee1f2018a223fc937fe05c93644110 Mon Sep 17 00:00:00 2001 From: Andrzej Jackowski Date: Tue, 4 Nov 2025 12:38:36 +0100 Subject: [PATCH] auth: change return type of passwords::check to future MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new `passwords::hash_with_salt_async` and change the return type of `passwords::check` to `future`. This enables yielding during password computations later in this patch series. The old method, `hash_with_salt`, is marked as deprecated because new code should use the new `hash_with_salt_async` function. We are not removing `hash_with_salt` now to reduce the regression risk of changing the hashing implementation—at least the methods that change persistent hashes (CREATE, ALTER) will continue to use the old hashing method. However, in the future, `hash_with_salt` should be entirely removed. Refs: scylladb/scylladb#26859 --- auth/password_authenticator.cc | 2 +- auth/passwords.cc | 25 +++++++++++++++++++++++-- auth/passwords.hh | 13 +++++++++++-- test/boost/auth_passwords_test.cc | 6 ++---- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/auth/password_authenticator.cc b/auth/password_authenticator.cc index 4868ae55ad..f589aa6914 100644 --- a/auth/password_authenticator.cc +++ b/auth/password_authenticator.cc @@ -328,7 +328,7 @@ future password_authenticator::authenticate( } salted_hash = role->salted_hash; } - const bool password_match = passwords::check(password, *salted_hash); + const bool password_match = co_await passwords::check(password, *salted_hash); if (!password_match) { throw exceptions::authentication_exception("Username and/or password are incorrect"); } diff --git a/auth/passwords.cc b/auth/passwords.cc index f047648a3b..8effdc1505 100644 --- a/auth/passwords.cc +++ b/auth/passwords.cc @@ -7,6 +7,8 @@ */ #include "auth/passwords.hh" +#include "utils/crypt_sha512.hh" +#include #include @@ -45,6 +47,24 @@ sstring hash_with_salt(const sstring& pass, const sstring& salt) { return res; } +seastar::future hash_with_salt_async(const sstring& pass, const sstring& salt) { + sstring res; + // Only SHA-512 hashes for passphrases shorter than 256 bytes can be computed using + // the __crypt_sha512 method. For other computations, we fall back to the + // crypt_r implementation from ``, which can stall. + if (salt.starts_with(prefix_for_scheme(scheme::sha_512)) && pass.size() <= 255) { + char buf[128]; + const char * output_ptr = co_await __crypt_sha512(pass.c_str(), salt.c_str(), buf); + verify_hashing_output(output_ptr); + res = output_ptr; + } else { + const char * output_ptr = crypt_r(pass.c_str(), salt.c_str(), &tlcrypt); + verify_hashing_output(output_ptr); + res = output_ptr; + } + co_return res; +} + std::string_view prefix_for_scheme(scheme c) noexcept { switch (c) { case scheme::bcrypt_y: return "$2y$"; @@ -61,8 +81,9 @@ no_supported_schemes::no_supported_schemes() : std::runtime_error("No allowed hashing schemes are supported on this system") { } -bool check(const sstring& pass, const sstring& salted_hash) { - return detail::hash_with_salt(pass, salted_hash) == salted_hash; +seastar::future check(const sstring& pass, const sstring& salted_hash) { + const auto pwd_hash = co_await detail::hash_with_salt_async(pass, salted_hash); + co_return pwd_hash == salted_hash; } } // namespace auth::passwords diff --git a/auth/passwords.hh b/auth/passwords.hh index 617f55ac13..7b0c769f75 100644 --- a/auth/passwords.hh +++ b/auth/passwords.hh @@ -11,6 +11,7 @@ #include #include +#include #include #include "seastarx.hh" @@ -75,10 +76,18 @@ sstring generate_salt(RandomNumberEngine& g, scheme scheme) { /// /// Hash a password combined with an implementation-specific salt string. +/// Deprecated in favor of `hash_with_salt_async`. /// /// \throws \ref std::system_error when an unexpected implementation-specific error occurs. /// -sstring hash_with_salt(const sstring& pass, const sstring& salt); +[[deprecated("Use hash_with_salt_async instead")]] sstring hash_with_salt(const sstring& pass, const sstring& salt); + +/// +/// Async version of `hash_with_salt` that returns a future. +/// +/// \throws \ref std::system_error when an unexpected implementation-specific error occurs. +/// +seastar::future hash_with_salt_async(const sstring& pass, const sstring& salt); } // namespace detail @@ -107,6 +116,6 @@ sstring hash(const sstring& pass, RandomNumberEngine& g, scheme scheme) { /// /// \throws \ref std::system_error when an unexpected implementation-specific error occurs. /// -bool check(const sstring& pass, const sstring& salted_hash); +seastar::future check(const sstring& pass, const sstring& salted_hash); } // namespace auth::passwords diff --git a/test/boost/auth_passwords_test.cc b/test/boost/auth_passwords_test.cc index 32f50e336e..f3396a3411 100644 --- a/test/boost/auth_passwords_test.cc +++ b/test/boost/auth_passwords_test.cc @@ -54,9 +54,8 @@ SEASTAR_TEST_CASE(correct_passwords_authenticate) { }; for (const char* p : passwords) { - BOOST_REQUIRE(auth::passwords::check(p, auth::passwords::hash(p, rng_for_salt, auth::passwords::scheme::sha_512))); + BOOST_REQUIRE(co_await auth::passwords::check(p, auth::passwords::hash(p, rng_for_salt, auth::passwords::scheme::sha_512))); } - co_return; } // @@ -64,6 +63,5 @@ SEASTAR_TEST_CASE(correct_passwords_authenticate) { // SEASTAR_TEST_CASE(incorrect_passwords_do_not_authenticate) { const sstring hashed_password = auth::passwords::hash("actual_password", rng_for_salt,auth::passwords::scheme::sha_512); - BOOST_REQUIRE(!auth::passwords::check("password_guess", hashed_password)); - co_return; + BOOST_REQUIRE(!co_await auth::passwords::check("password_guess", hashed_password)); }