_site/cover/msmp_handshake_response.COVER.html

1 %% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(msmp_handshake_response).
17
18
19 -feature(maybe_expr, enable).
20
21
22 -export([auth_response/2]).
23 -export([decode/0]).
24 -export([encode/0]).
25
26
27 -spec decode() -> scran:parser(binary(), msmp:handshake_response()).
28
29 decode() ->
30 7 fun
31 (Input) ->
32 8 (scran_sequence:combined_with(
33 scran_result:into_map(
34 scran_sequence:sequence(
35 [scran_result:kv(
36 client_flags,
37 msmp_capabilities:decode(combined)),
38
39 scran_result:kv(
40 max_packet_size,
41 msmp_integer_fixed:decode(4)),
42
43 scran_result:kv(
44 character_set,
45 msmp_integer_fixed:decode(1)),
46
47 scran_branch:alt(
48 [scran_result:kv(
49 action,
50 scran_combinator:value(
51 handshake_response,
52 scran_bytes:tag(<<0:23/unit:8>>))),
53
54 scran_sequence:sequence(
55 [scran_result:kv(
56 action,
57 scran_combinator:value(
58 handshake_response,
59 scran_bytes:tag(<<0:19/unit:8>>))),
60
61 scran_result:kv(
62 extended_capabilities,
63 msmp_integer_fixed:decode(4))])]),
64
65 scran_result:kv(
66 username,
67 msmp_string_null_terminated:decode())])),
68 fun
69 (#{client_flags := #{plugin_auth_lenenc_client_data := LengthEncoded,
70 connect_with_db := ConnectWithDB,
71 plugin_auth := PluginAuth,
72 connect_attrs := ConnectAttrs,
73 zstd_compression := ZStdCompression}}) ->
74 8 scran_result:into_map(
75 scran_sequence:sequence(
76 [scran_combinator:condition(
77 LengthEncoded,
78 scran_result:kv(
79 auth_response,
80 msmp_string_length_encoded:decode())),
81
82 scran_combinator:condition(
83 ConnectWithDB,
84 scran_result:kv(
85 database,
86 msmp_string_null_terminated:decode())),
87
88 scran_combinator:condition(
89 PluginAuth,
90 scran_result:kv(
91 client_plugin_name,
92 scran_result:into_atom(
93 msmp_string_null_terminated:decode()))),
94
95 scran_combinator:condition(
96 ConnectAttrs,
97 scran_result:kv(
98 connect_attrs,
99 scran_result:into_map(
100 scran_combinator:map_parser(
101 scran_bytes:take(msmp_integer_variable:decode()),
102 scran_multi:many1(
103 scran_result:into_tuple(
104 scran_sequence:sequence(
105 [msmp_string_length_encoded:decode(),
106 msmp_string_length_encoded:decode()]))))))),
107
108 scran_combinator:condition(
109 ZStdCompression,
110 scran_result:kv(
111 zstd_compression_level,
112 msmp_integer_fixed:decode(1)))]))
113 end))(Input)
114 end.
115
116
117 encode() ->
118 4 fun
119 (#{action := handshake_response,
120 extended_capabilities := _,
121 client_flags := #{plugin_auth_lenenc_client_data := LengthEncoded,
122 connect_with_db := ConnectWithDB,
123 plugin_auth := PluginAuth,
124 connect_attrs := ConnectAttrs,
125 zstd_compression := ZStdCompression}} = Decoded) ->
126 1 (narcs_sequence:sequence(
127 [narcs_combinator:v(
128 client_flags,
129 msmp_capabilities:encode(combined)),
130 narcs_combinator:v(
131 max_packet_size,
132 msmp_integer_fixed:encode(4)),
133 narcs_combinator:v(
134 character_set,
135 msmp_integer_fixed:encode(1)),
136 narcs_bytes:tag(<<0:19/unit:8>>),
137 narcs_combinator:v(
138 extended_capabilities,
139 msmp_integer_fixed:encode(4)),
140 narcs_combinator:v(
141 username,
142 msmp_string_null_terminated:encode()),
143
144 narcs_combinator:condition(
145 LengthEncoded,
146 narcs_combinator:v(
147 auth_response,
148 msmp_string_length_encoded:encode())),
149
150 narcs_combinator:condition(
151 ConnectWithDB,
152 narcs_combinator:v(
153 database,
154 msmp_string_null_terminated:encode())),
155
156 narcs_combinator:condition(
157 PluginAuth,
158 narcs_combinator:v(
159 client_plugin_name,
160 narcs_combinator:map_result(
161 narcs_bytes:from_atom(),
162 narcs_bytes:null_terminated()))),
163
164 narcs_combinator:condition(
165 ConnectAttrs,
166 narcs_combinator:v(
167 connect_attrs,
168 narcs_combinator:map_result(
169 narcs_sequence:sequence(
170 msmp_string_length_encoded:encode(),
171 msmp_string_length_encoded:encode()),
172 narcs_bytes:length_encoded(
173 msmp_integer_variable:encode())))),
174
175 narcs_combinator:condition(
176 ZStdCompression,
177 narcs_combinator:v(
178 zstd_compression_level,
179 msmp_integer_fixed:encode(1)))]))(Decoded);
180
181
182 (#{action := handshake_response,
183 client_flags := #{plugin_auth_lenenc_client_data := LengthEncoded,
184 connect_with_db := ConnectWithDB,
185 plugin_auth := PluginAuth,
186 connect_attrs := ConnectAttrs,
187 zstd_compression := ZStdCompression}} = Decoded) ->
188 3 (narcs_sequence:sequence(
189 [narcs_combinator:v(
190 client_flags,
191 msmp_capabilities:encode(combined)),
192 narcs_combinator:v(
193 max_packet_size,
194 msmp_integer_fixed:encode(4)),
195 narcs_combinator:v(
196 character_set,
197 msmp_integer_fixed:encode(1)),
198 narcs_bytes:tag(<<0:23/unit:8>>),
199 narcs_combinator:v(
200 username,
201 msmp_string_null_terminated:encode()),
202
203 narcs_combinator:condition(
204 LengthEncoded,
205 narcs_combinator:v(
206 auth_response,
207 msmp_string_length_encoded:encode())),
208
209 narcs_combinator:condition(
210 ConnectWithDB,
211 narcs_combinator:v(
212 database,
213 msmp_string_null_terminated:encode())),
214
215 narcs_combinator:condition(
216 PluginAuth,
217 narcs_combinator:v(
218 client_plugin_name,
219 narcs_combinator:map_result(
220 narcs_bytes:from_atom(),
221 narcs_bytes:null_terminated()))),
222
223 narcs_combinator:condition(
224 ConnectAttrs,
225 narcs_combinator:v(
226 connect_attrs,
227 narcs_combinator:map_result(
228 narcs_sequence:sequence(
229 msmp_string_length_encoded:encode(),
230 msmp_string_length_encoded:encode()),
231 narcs_bytes:length_encoded(
232 msmp_integer_variable:encode())))),
233
234 narcs_combinator:condition(
235 ZStdCompression,
236 narcs_combinator:v(
237 zstd_compression_level,
238 msmp_integer_fixed:encode(1)))]))(Decoded);
239 (_) ->
240
:-(
nomatch
241 end.
242
243
244 auth_response(#{auth_plugin_name := mysql_native_password,
245 auth_plugin_data_part_1 := <<AuthDataPartOne:8/bytes>>,
246 auth_plugin_data_part_2 := <<AuthDataPartTwo:12/bytes, 0>>},
247 Password) ->
248 2 mysql_native_password([AuthDataPartOne, AuthDataPartTwo], Password);
249
250 auth_response(#{auth_plugin_name := caching_sha2_password,
251 auth_plugin_data_part_1 := <<AuthDataPartOne:8/bytes>>,
252 auth_plugin_data_part_2 := <<AuthDataPartTwo:12/bytes, 0>>},
253 Password) ->
254 1 caching_sha2_password([AuthDataPartOne, AuthDataPartTwo], Password);
255
256 auth_response(#{plugin_name := mysql_native_password,
257 plugin_provided_data := <<PluginProvidedData:20/bytes, 0>>},
258 Password) ->
259
:-(
mysql_native_password(PluginProvidedData, Password);
260
261 auth_response(#{plugin_name := caching_sha2_password,
262 plugin_provided_data := <<PluginProvidedData:20/bytes, 0>>},
263 Password) ->
264
:-(
mysql_native_password(PluginProvidedData, Password).
265
266
267 mysql_native_password(PluginProvidedData, Password) ->
268 2 crypto:exor(
269 crypto:hash(sha, Password),
270 crypto:hash(sha,
271 [PluginProvidedData,
272 crypto:hash(
273 sha,
274 crypto:hash(
275 sha,
276 Password))])).
277
278 caching_sha2_password(PluginProvidedData, Password) ->
279 1 crypto:exor(
280 crypto:hash(sha256, Password),
281 crypto:hash(sha256,
282 [crypto:hash(sha256, crypto:hash(sha256, Password)),
283 PluginProvidedData])).
Line Hits Source