1use sha1::Sha1;
10use sha2::{Digest, Sha256};
11
12fn xor<T, U>(mut left: T, right: U) -> T
13where
14 T: AsMut<[u8]>,
15 U: AsRef<[u8]>,
16{
17 left.as_mut()
18 .iter_mut()
19 .zip(right.as_ref().iter())
20 .map(|(l, r)| *l ^= r)
21 .last();
22 left
23}
24
25fn to_u8_32(bytes: impl AsRef<[u8]>) -> [u8; 32] {
26 let mut out = [0; 32];
27 out[..].copy_from_slice(bytes.as_ref());
28 out
29}
30
31fn hash_password(output: &mut [u32; 2], password: &[u8]) {
33 let mut nr: u32 = 1345345333;
34 let mut add: u32 = 7;
35 let mut nr2: u32 = 0x12345671;
36
37 let mut tmp: u32;
38
39 for x in password {
40 if *x == b' ' || *x == b'\t' {
41 continue;
42 }
43
44 tmp = *x as u32;
45 nr ^= (nr & 63)
46 .wrapping_add(add)
47 .wrapping_mul(tmp)
48 .wrapping_add(nr << 8);
49 nr2 = nr2.wrapping_add((nr2 << 8) ^ nr);
50 add = add.wrapping_add(tmp);
51 }
52
53 output[0] = nr & 0b01111111_11111111_11111111_11111111;
54 output[1] = nr2 & 0b01111111_11111111_11111111_11111111;
55}
56
57pub fn scramble_323(nonce: &[u8], password: &[u8]) -> Option<[u8; 8]> {
58 struct Rand323 {
59 seed1: u32,
60 seed2: u32,
61 max_value: u32,
62 max_value_dbl: f64,
63 }
64
65 impl Rand323 {
66 fn init(seed1: u32, seed2: u32) -> Self {
67 Self {
68 max_value: 0x3FFFFFFF,
69 max_value_dbl: 0x3FFFFFFF as f64,
70 seed1: seed1 % 0x3FFFFFFF,
71 seed2: seed2 % 0x3FFFFFFF,
72 }
73 }
74
75 fn my_rnd(&mut self) -> f64 {
76 self.seed1 = (self.seed1 * 3 + self.seed2) % self.max_value;
77 self.seed2 = (self.seed1 + self.seed2 + 33) % self.max_value;
78 (self.seed1 as f64) / self.max_value_dbl
79 }
80 }
81
82 let mut hash_pass = [0_u32; 2];
83 let mut hash_message = [0_u32; 2];
84
85 if password.is_empty() {
86 return None;
87 }
88
89 let mut output = [0_u8; 8];
90
91 hash_password(&mut hash_pass, password);
92 hash_password(&mut hash_message, nonce);
93
94 let mut rand_st = Rand323::init(
95 hash_pass[0] ^ hash_message[0],
96 hash_pass[1] ^ hash_message[1],
97 );
98
99 for x in output.iter_mut() {
100 *x = ((rand_st.my_rnd() * 31_f64).floor() + 64_f64) as u8;
101 }
102
103 let extra = (rand_st.my_rnd() * 31_f64).floor() as u8;
104
105 for x in output.iter_mut() {
106 *x ^= extra;
107 }
108
109 Some(output)
110}
111
112pub fn scramble_native(nonce: &[u8], password: &[u8]) -> Option<[u8; 20]> {
116 fn sha1_1(bytes: impl AsRef<[u8]>) -> [u8; 20] {
117 Sha1::digest(bytes).into()
118 }
119
120 fn sha1_2(bytes1: impl AsRef<[u8]>, bytes2: impl AsRef<[u8]>) -> [u8; 20] {
121 let mut hasher = Sha1::new();
122 hasher.update(bytes1.as_ref());
123 hasher.update(bytes2.as_ref());
124 hasher.finalize().into()
125 }
126
127 if password.is_empty() {
128 return None;
129 }
130
131 Some(xor(
132 sha1_1(password),
133 sha1_2(nonce, sha1_1(sha1_1(password))),
134 ))
135}
136
137pub fn scramble_sha256(nonce: &[u8], password: &[u8]) -> Option<[u8; 32]> {
141 fn sha256_1(bytes: impl AsRef<[u8]>) -> [u8; 32] {
142 let mut hasher = Sha256::default();
143 hasher.update(bytes.as_ref());
144 to_u8_32(hasher.finalize())
145 }
146
147 fn sha256_2(bytes1: impl AsRef<[u8]>, bytes2: impl AsRef<[u8]>) -> [u8; 32] {
148 let mut hasher = Sha256::default();
149 hasher.update(bytes1.as_ref());
150 hasher.update(bytes2.as_ref());
151 to_u8_32(hasher.finalize())
152 }
153
154 if password.is_empty() {
155 return None;
156 }
157
158 Some(xor(
159 sha256_1(password),
160 sha256_2(sha256_1(sha256_1(password)), nonce),
161 ))
162}
163
164#[cfg_attr(docsrs, doc(cfg(feature = "client_ed25519")))]
168pub fn create_response_for_ed25519(_pass: &[u8], _message: &[u8]) -> [u8; 64] {
169 #[cfg(not(feature = "client_ed25519"))]
170 {
171 panic!(
172 "Can't create response for `ed25519` authentication plugin — `mysql_common/client_ed25519` feature is disabled."
173 )
174 }
175
176 #[cfg(feature = "client_ed25519")]
177 {
178 use curve25519_dalek::scalar::clamp_integer;
179 use curve25519_dalek::{EdwardsPoint, Scalar};
180 use sha2::Sha512;
181 use std::convert::TryInto;
182
183 let hashed_pass = Sha512::default().chain_update(_pass).finalize();
189
190 let secret: &[u8; 32] = &hashed_pass[..32].try_into().unwrap();
192 let hash_prefix: &[u8; 32] = &hashed_pass[32..].try_into().unwrap();
193
194 let small_s = clamp_integer(*secret);
196 let reduced_small_s = Scalar::from_bytes_mod_order(small_s);
197 let capital_a = EdwardsPoint::mul_base(&reduced_small_s).compress();
198
199 let small_r = Sha512::default()
203 .chain_update(hash_prefix)
204 .chain_update(_message)
205 .finalize();
206
207 let reduced_small_r = Scalar::from_bytes_mod_order_wide(small_r.as_ref());
209
210 let capital_r = EdwardsPoint::mul_base(&reduced_small_r).compress();
212
213 let small_k = Sha512::default()
217 .chain_update(&capital_r.to_bytes())
218 .chain_update(&capital_a.to_bytes())
219 .chain_update(_message)
220 .finalize();
221
222 let reduced_small_k = Scalar::from_bytes_mod_order_wide(small_k.as_ref());
224
225 let capital_s = reduced_small_k * reduced_small_s;
226 let capital_s = capital_s + reduced_small_r;
227
228 let mut result = [0; 64];
233 result[..32].copy_from_slice(capital_r.as_bytes());
234 result[32..].copy_from_slice(capital_s.as_bytes());
235
236 result
237 }
238}
239
240#[cfg_attr(docsrs, doc(cfg(feature = "client_parsec")))]
244pub fn create_response_for_parsec(
245 _pass: &[u8],
246 _server_nonce: &[u8; 32],
247 _iterations: u32,
248 _server_salt: &[u8],
249) -> [u8; 96] {
250 #[cfg(not(feature = "client_parsec"))]
251 {
252 panic!(
253 "Can't create response for `parsec` authentication plugin — `mysql_common/client_parsec` feature is disabled."
254 )
255 }
256
257 #[cfg(feature = "client_parsec")]
258 {
259 use ed25519_dalek::{Signer, SigningKey};
260 use pbkdf2::pbkdf2_hmac;
261 use rand::Rng;
262 use sha2::Sha512;
263
264 let mut derived_key = [0u8; 32];
266 pbkdf2_hmac::<Sha512>(_pass, _server_salt, _iterations, &mut derived_key);
267
268 let mut rng = rand::rng();
269 let mut msg_and_signature = [0u8; 128];
272 msg_and_signature[..32].copy_from_slice(_server_nonce);
273 let client_nonce = &mut msg_and_signature[32..64];
275 rng.fill(client_nonce);
276
277 let signing_key = SigningKey::from_bytes(&derived_key);
279
280 let signature = signing_key.sign(&msg_and_signature[..64]);
281 msg_and_signature[64..].copy_from_slice(&signature.to_bytes());
282 msg_and_signature[32..].try_into().expect("infallible")
283 }
284}
285
286#[cfg(test)]
287mod test {
288 use super::*;
289
290 #[test]
291 fn should_compute_scrambled_password() {
292 let scr = [
293 0x4e, 0x52, 0x33, 0x48, 0x50, 0x3a, 0x71, 0x49, 0x59, 0x61, 0x5f, 0x39, 0x3d, 0x64,
294 0x62, 0x3f, 0x53, 0x64, 0x7b, 0x60,
295 ];
296 let password = [0x47, 0x21, 0x69, 0x64, 0x65, 0x72, 0x32, 0x37];
297 let output1 = scramble_native(&scr, &password);
298 let output2 = scramble_sha256(&scr, &password);
299 assert!(output1.is_some());
300 assert!(output2.is_some());
301 assert_eq!(
302 output1.unwrap(),
303 [
304 0x09, 0xcf, 0xf8, 0x85, 0x5e, 0x9e, 0x70, 0x53, 0x40, 0xff, 0x22, 0x70, 0xd8, 0xfb,
305 0x9f, 0xad, 0xba, 0x90, 0x6b, 0x70,
306 ]
307 );
308 assert_eq!(
309 output2.unwrap(),
310 [
311 0x4f, 0x97, 0xbb, 0xfd, 0x20, 0x24, 0x01, 0xc4, 0x2a, 0x69, 0xde, 0xaa, 0xe5, 0x3b,
312 0xda, 0x07, 0x7e, 0xd7, 0x57, 0x85, 0x63, 0xc1, 0xa8, 0x0e, 0xb8, 0x16, 0xc8, 0x21,
313 0x19, 0xb6, 0x8d, 0x2e,
314 ]
315 );
316 }
317}