18
18
#include < boost/mysql/impl/internal/protocol/impl/protocol_types.hpp>
19
19
#include < boost/mysql/impl/internal/protocol/impl/serialization_context.hpp>
20
20
#include < boost/mysql/impl/internal/protocol/static_buffer.hpp>
21
+ #include < boost/mysql/impl/internal/sansio/auth_plugin_common.hpp>
21
22
#include < boost/mysql/impl/internal/sansio/connection_state_data.hpp>
23
+ #include < boost/mysql/impl/internal/sansio/csha2p_encrypt_password.hpp>
22
24
25
+ #include < boost/asio/ssl/error.hpp>
26
+ #include < boost/container/small_vector.hpp>
23
27
#include < boost/core/span.hpp>
24
28
#include < boost/system/result.hpp>
29
+ #include < boost/system/system_category.hpp>
25
30
26
31
#include < array>
32
+ #include < cstddef>
27
33
#include < cstdint>
28
34
#include < openssl/sha.h>
29
35
@@ -35,59 +41,53 @@ namespace mysql {
35
41
namespace detail {
36
42
37
43
// Constants
38
- BOOST_INLINE_CONSTEXPR std::size_t csha2p_challenge_length = 20 ;
39
- BOOST_INLINE_CONSTEXPR std::size_t csha2p_response_length = 32 ;
44
+ BOOST_INLINE_CONSTEXPR std::size_t csha2p_hash_size = 32 ;
40
45
BOOST_INLINE_CONSTEXPR const char * csha2p_plugin_name = " caching_sha2_password" ;
46
+ static_assert (csha2p_hash_size <= max_hash_size, " " );
47
+ static_assert (csha2p_hash_size == SHA256_DIGEST_LENGTH, " Buffer size mismatch" );
41
48
42
49
inline void csha2p_hash_password_impl (
43
50
string_view password,
44
- span<const std::uint8_t , csha2p_challenge_length> challenge ,
45
- span<std::uint8_t , csha2p_response_length > output
51
+ span<const std::uint8_t , scramble_size> scramble ,
52
+ span<std::uint8_t , csha2p_hash_size > output
46
53
)
47
54
{
48
- static_assert (csha2p_response_length == SHA256_DIGEST_LENGTH, " Buffer size mismatch" );
49
-
50
- // SHA(SHA(password_sha) concat challenge) XOR password_sha
55
+ // SHA(SHA(password_sha) concat scramble) XOR password_sha
51
56
// hash1 = SHA(pass)
52
- std::array<std::uint8_t , csha2p_response_length > password_sha;
57
+ std::array<std::uint8_t , csha2p_hash_size > password_sha;
53
58
SHA256 (reinterpret_cast <const unsigned char *>(password.data ()), password.size (), password_sha.data ());
54
59
55
- // SHA(password_sha) concat challenge = buffer
56
- std::array<std::uint8_t , csha2p_response_length + csha2p_challenge_length > buffer;
60
+ // SHA(password_sha) concat scramble = buffer
61
+ std::array<std::uint8_t , csha2p_hash_size + scramble_size > buffer;
57
62
SHA256 (password_sha.data (), password_sha.size (), buffer.data ());
58
- std::memcpy (buffer.data () + csha2p_response_length, challenge .data (), csha2p_challenge_length );
63
+ std::memcpy (buffer.data () + csha2p_hash_size, scramble .data (), scramble. size () );
59
64
60
- // SHA(SHA(password_sha) concat challenge ) = SHA(buffer) = salted_password
61
- std::array<std::uint8_t , csha2p_response_length > salted_password;
65
+ // SHA(SHA(password_sha) concat scramble ) = SHA(buffer) = salted_password
66
+ std::array<std::uint8_t , csha2p_hash_size > salted_password;
62
67
SHA256 (buffer.data (), buffer.size (), salted_password.data ());
63
68
64
69
// salted_password XOR password_sha
65
- for (unsigned i = 0 ; i < csha2p_response_length ; ++i)
70
+ for (unsigned i = 0 ; i < csha2p_hash_size ; ++i)
66
71
{
67
72
output[i] = salted_password[i] ^ password_sha[i];
68
73
}
69
74
}
70
75
71
- inline system::result< static_buffer<32 > > csha2p_hash_password (
76
+ inline static_buffer<max_hash_size > csha2p_hash_password (
72
77
string_view password,
73
- span<const std::uint8_t > challenge
78
+ span<const std::uint8_t , scramble_size> scramble
74
79
)
75
80
{
76
- // If the challenge doesn't match the expected size,
77
- // something wrong is going on and we should fail
78
- if (challenge.size () != csha2p_challenge_length)
79
- return client_errc::protocol_value_error;
80
-
81
81
// Empty passwords are not hashed
82
82
if (password.empty ())
83
83
return {};
84
84
85
85
// Run the algorithm
86
- static_buffer<32 > res (csha2p_response_length );
86
+ static_buffer<max_hash_size > res (csha2p_hash_size );
87
87
csha2p_hash_password_impl (
88
88
password,
89
- span< const std:: uint8_t , csha2p_challenge_length>(challenge) ,
90
- span<std::uint8_t , csha2p_response_length >(res.data (), csha2p_response_length )
89
+ scramble ,
90
+ span<std::uint8_t , csha2p_hash_size >(res.data (), csha2p_hash_size )
91
91
);
92
92
return res;
93
93
}
@@ -106,13 +106,32 @@ class csha2p_algo
106
106
return server_data.size () == 1u && server_data[0 ] == 3 ;
107
107
}
108
108
109
+ static next_action encrypt_password (
110
+ connection_state_data& st,
111
+ std::uint8_t & seqnum,
112
+ string_view password,
113
+ span<const std::uint8_t , scramble_size> scramble,
114
+ span<const std::uint8_t > server_key
115
+ )
116
+ {
117
+ container::small_vector<std::uint8_t , 512 > buff;
118
+ auto ec = csha2p_encrypt_password (password, scramble, server_key, buff, asio::error::ssl_category);
119
+ if (ec)
120
+ return ec;
121
+ return st.write (
122
+ string_eof{string_view (reinterpret_cast <const char *>(buff.data ()), buff.size ())},
123
+ seqnum
124
+ );
125
+ }
126
+
109
127
public:
110
128
csha2p_algo () = default ;
111
129
112
130
next_action resume (
113
131
connection_state_data& st,
114
132
span<const std::uint8_t > server_data,
115
133
string_view password,
134
+ span<const std::uint8_t , scramble_size> scramble,
116
135
bool secure_channel,
117
136
std::uint8_t & seqnum
118
137
)
@@ -124,19 +143,34 @@ class csha2p_algo
124
143
// or told us to read again because an OK packet or error packet is coming.
125
144
if (is_perform_full_auth (server_data))
126
145
{
127
- // At this point, we don't support full auth over insecure channels
128
- if (!secure_channel)
146
+ if (secure_channel)
129
147
{
130
- return make_error_code (client_errc::auth_plugin_requires_ssl);
131
- }
148
+ // We should send a packet with just the password, as a NULL-terminated string
149
+ BOOST_MYSQL_YIELD (resume_point_, 1 , st. write (string_null{password}, seqnum))
132
150
133
- // We should send a packet with just the password, as a NULL-terminated string
134
- BOOST_MYSQL_YIELD (resume_point_, 1 , st.write (string_null{password}, seqnum))
151
+ // The server shouldn't send us any more packets
152
+ return error_code (client_errc::bad_handshake_packet_type);
153
+ }
154
+ else
155
+ {
156
+ // Request the server's public key
157
+ BOOST_MYSQL_YIELD (resume_point_, 2 , st.write (int1{2 }, seqnum))
158
+
159
+ // Encrypt the password with the key we were given
160
+ BOOST_MYSQL_YIELD (
161
+ resume_point_,
162
+ 3 ,
163
+ encrypt_password (st, seqnum, password, scramble, server_data)
164
+ )
165
+
166
+ // The server shouldn't send us any more packets
167
+ return error_code (client_errc::bad_handshake_packet_type);
168
+ }
135
169
}
136
170
else if (is_fast_auth_ok (server_data))
137
171
{
138
172
// We should wait for the server to send an OK or an error
139
- BOOST_MYSQL_YIELD (resume_point_, 2 , st.read (seqnum))
173
+ BOOST_MYSQL_YIELD (resume_point_, 4 , st.read (seqnum))
140
174
}
141
175
else
142
176
{
0 commit comments