File indexing completed on 2026-08-17 08:51:24
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_CSHA2P_ENCRYPT_PASSWORD_HPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_CSHA2P_ENCRYPT_PASSWORD_HPP
0010
0011
0012
0013 #include <boost/mysql/client_errc.hpp>
0014 #include <boost/mysql/error_code.hpp>
0015 #include <boost/mysql/string_view.hpp>
0016
0017 #include <boost/mysql/impl/internal/sansio/auth_plugin_common.hpp>
0018
0019 #include <boost/assert/source_location.hpp>
0020 #include <boost/container/small_vector.hpp>
0021 #include <boost/core/span.hpp>
0022 #include <boost/system/error_category.hpp>
0023 #include <boost/system/system_category.hpp>
0024
0025 #include <cstdint>
0026 #include <memory>
0027 #include <openssl/bio.h>
0028 #include <openssl/err.h>
0029 #include <openssl/evp.h>
0030 #include <openssl/pem.h>
0031 #include <openssl/rsa.h>
0032
0033 namespace boost {
0034 namespace mysql {
0035 namespace detail {
0036
0037
0038
0039 inline error_code translate_openssl_error(
0040 unsigned long code,
0041 const source_location* loc,
0042 const system::error_category& openssl_category
0043 )
0044 {
0045
0046
0047 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
0048 if (ERR_SYSTEM_ERROR(code))
0049 {
0050 return error_code(ERR_GET_REASON(code), system::system_category(), loc);
0051 }
0052 #endif
0053
0054
0055
0056
0057
0058 int int_code = static_cast<int>(code);
0059
0060
0061
0062
0063 if (int_code == 0)
0064 return error_code(client_errc::unknown_openssl_error, loc);
0065 else
0066 return error_code(int_code, openssl_category, loc);
0067 }
0068
0069 inline container::small_vector<std::uint8_t, 512> csha2p_salt_password(
0070 string_view password,
0071 span<const std::uint8_t, scramble_size> scramble
0072 )
0073 {
0074
0075 container::small_vector<std::uint8_t, 512> res(password.size() + 1u, 0);
0076 for (std::size_t i = 0; i < password.size(); ++i)
0077 res[i] = password[i] ^ scramble[i % scramble.size()];
0078
0079
0080
0081 res[password.size()] = scramble[password.size() % scramble.size()];
0082
0083 return res;
0084 }
0085
0086 inline error_code csha2p_encrypt_password(
0087 string_view password,
0088 span<const std::uint8_t, scramble_size> scramble,
0089 span<const std::uint8_t> server_key,
0090 container::small_vector<std::uint8_t, 512>& output,
0091 const system::error_category& openssl_category
0092 )
0093 {
0094
0095 struct bio_deleter
0096 {
0097 void operator()(BIO* bio) const noexcept { BIO_free(bio); }
0098 };
0099 using unique_bio = std::unique_ptr<BIO, bio_deleter>;
0100
0101 struct evp_pkey_deleter
0102 {
0103 void operator()(EVP_PKEY* pkey) const noexcept { EVP_PKEY_free(pkey); }
0104 };
0105 using unique_evp_pkey = std::unique_ptr<EVP_PKEY, evp_pkey_deleter>;
0106
0107 struct evp_pkey_ctx_deleter
0108 {
0109 void operator()(EVP_PKEY_CTX* ctx) const noexcept { EVP_PKEY_CTX_free(ctx); }
0110 };
0111 using unique_evp_pkey_ctx = std::unique_ptr<EVP_PKEY_CTX, evp_pkey_ctx_deleter>;
0112
0113
0114 constexpr std::size_t max_key_buffer_size = 1024u * 1024u;
0115 if (server_key.size() > max_key_buffer_size)
0116 {
0117 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0118 return error_code(client_errc::protocol_value_error, &loc);
0119 }
0120
0121
0122 unique_bio bio{BIO_new_mem_buf(server_key.data(), static_cast<int>(server_key.size()))};
0123 if (!bio)
0124 {
0125 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0126 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0127 }
0128 unique_evp_pkey key(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr));
0129 if (!key)
0130 {
0131 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0132 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0133 }
0134
0135
0136 auto salted_password = csha2p_salt_password(password, scramble);
0137
0138
0139 unique_evp_pkey_ctx ctx(EVP_PKEY_CTX_new(key.get(), nullptr));
0140 if (!ctx)
0141 {
0142 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0143 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0144 }
0145 if (EVP_PKEY_encrypt_init(ctx.get()) <= 0)
0146 {
0147 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0148 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0149 }
0150 int rsa_pad_res = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
0151 if (rsa_pad_res <= 0)
0152 {
0153
0154
0155
0156
0157 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0158 if (rsa_pad_res == -2)
0159 return error_code(client_errc::protocol_value_error, &loc);
0160 else
0161 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0162 }
0163
0164
0165 int max_size = EVP_PKEY_size(key.get());
0166 if (max_size <= 0)
0167 {
0168 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0169 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0170 }
0171 output.resize(max_size);
0172
0173
0174 std::size_t actual_size = static_cast<std::size_t>(max_size);
0175 if (EVP_PKEY_encrypt(
0176 ctx.get(),
0177 output.data(),
0178 &actual_size,
0179 salted_password.data(),
0180 salted_password.size()
0181 ) <= 0)
0182 {
0183 static constexpr auto loc = BOOST_CURRENT_LOCATION;
0184 return translate_openssl_error(ERR_get_error(), &loc, openssl_category);
0185 }
0186
0187
0188 BOOST_ASSERT(actual_size <= output.size());
0189 output.resize(actual_size);
0190
0191
0192 return error_code();
0193 }
0194
0195 }
0196 }
0197 }
0198
0199 #endif