postgres_protocol_sm3/password/
mod.rs1use crate::authentication::sasl;
10use base64::display::Base64Display;
11use base64::engine::general_purpose::STANDARD;
12use hmac::{Hmac, Mac};
13use md5::Md5;
14use sm3::Sm3;
15use rand::RngCore;
16use sha2::digest::FixedOutput;
17use sha2::{Digest, Sha256};
18
19#[cfg(test)]
20mod test;
21
22const SCRAM_DEFAULT_ITERATIONS: u32 = 4096;
23const SCRAM_DEFAULT_SALT_LEN: usize = 16;
24
25pub fn scram_sha_256(password: &[u8]) -> String {
31 let mut salt: [u8; SCRAM_DEFAULT_SALT_LEN] = [0; SCRAM_DEFAULT_SALT_LEN];
32 let mut rng = rand::thread_rng();
33 rng.fill_bytes(&mut salt);
34 scram_sha_256_salt(password, salt)
35}
36
37pub(crate) fn scram_sha_256_salt(password: &[u8], salt: [u8; SCRAM_DEFAULT_SALT_LEN]) -> String {
40 let prepared: Vec<u8> = match std::str::from_utf8(password) {
52 Ok(password_str) => {
53 match stringprep::saslprep(password_str) {
54 Ok(p) => p.into_owned().into_bytes(),
55 Err(_) => Vec::from(password),
57 }
58 }
59 Err(_) => Vec::from(password),
61 };
62
63 let salted_password = sasl::hi(&prepared, &salt, SCRAM_DEFAULT_ITERATIONS);
65
66 let mut hmac = Hmac::<Sha256>::new_from_slice(&salted_password)
68 .expect("HMAC is able to accept all key sizes");
69 hmac.update(b"Client Key");
70 let client_key = hmac.finalize().into_bytes();
71
72 let mut hash = Sha256::default();
74 hash.update(client_key.as_slice());
75 let stored_key = hash.finalize_fixed();
76
77 let mut hmac = Hmac::<Sha256>::new_from_slice(&salted_password)
79 .expect("HMAC is able to accept all key sizes");
80 hmac.update(b"Server Key");
81 let server_key = hmac.finalize().into_bytes();
82
83 format!(
84 "SCRAM-SHA-256${}:{}${}:{}",
85 SCRAM_DEFAULT_ITERATIONS,
86 Base64Display::new(&salt, &STANDARD),
87 Base64Display::new(&stored_key, &STANDARD),
88 Base64Display::new(&server_key, &STANDARD)
89 )
90}
91
92pub fn md5(password: &[u8], username: &str) -> String {
99 let mut salted_password = Vec::from(password);
101 salted_password.extend_from_slice(username.as_bytes());
102
103 let mut hash = Md5::new();
104 hash.update(&salted_password);
105 let digest = hash.finalize();
106 format!("md5{:x}", digest)
107}
108pub fn sm3(password: &[u8], username: &str) -> String {
113 let mut salted_password = Vec::from(password);
115 salted_password.extend_from_slice(username.as_bytes());
116
117 let mut hash = Sm3::new();
118 hash.update(&salted_password);
119 let digest = hash.finalize();
120 format!("sm3{:x}", digest)
121}