sqlx_postgres/message/
password.rsuse crate::io::BufMutExt;
use crate::message::{FrontendMessage, FrontendMessageFormat};
use md5::{Digest, Md5};
use sqlx_core::Error;
use std::fmt::Write;
use std::num::Saturating;
#[derive(Debug)]
pub enum Password<'a> {
Cleartext(&'a str),
Md5 {
password: &'a str,
username: &'a str,
salt: [u8; 4],
},
}
impl FrontendMessage for Password<'_> {
const FORMAT: FrontendMessageFormat = FrontendMessageFormat::PasswordPolymorphic;
#[inline(always)]
fn body_size_hint(&self) -> Saturating<usize> {
let mut size = Saturating(0);
match self {
Password::Cleartext(password) => {
size += password
.len()
.saturating_add(1) .checked_next_power_of_two()
.unwrap_or(usize::MAX);
}
Password::Md5 { .. } => {
size += 36;
}
}
size
}
fn encode_body(&self, buf: &mut Vec<u8>) -> Result<(), Error> {
match self {
Password::Cleartext(password) => {
buf.put_str_nul(password);
}
Password::Md5 {
username,
password,
salt,
} => {
let mut hasher = Md5::new();
hasher.update(password);
hasher.update(username);
let mut output = String::with_capacity(35);
let _ = write!(output, "{:x}", hasher.finalize_reset());
hasher.update(&output);
hasher.update(salt);
output.clear();
let _ = write!(output, "md5{:x}", hasher.finalize());
buf.put_str_nul(&output);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::message::FrontendMessage;
use super::Password;
#[test]
fn test_encode_clear_password() {
const EXPECTED: &[u8] = b"p\0\0\0\rpassword\0";
let mut buf = Vec::new();
let m = Password::Cleartext("password");
m.encode_msg(&mut buf).unwrap();
assert_eq!(buf, EXPECTED);
}
#[test]
fn test_encode_md5_password() {
const EXPECTED: &[u8] = b"p\0\0\0(md53e2c9d99d49b201ef867a36f3f9ed62c\0";
let mut buf = Vec::new();
let m = Password::Md5 {
password: "password",
username: "root",
salt: [147, 24, 57, 152],
};
m.encode_msg(&mut buf).unwrap();
assert_eq!(buf, EXPECTED);
}
#[cfg(all(test, not(debug_assertions)))]
#[bench]
fn bench_encode_clear_password(b: &mut test::Bencher) {
use test::black_box;
let mut buf = Vec::with_capacity(128);
b.iter(|| {
buf.clear();
black_box(Password::Cleartext("password")).encode_msg(&mut buf);
});
}
#[cfg(all(test, not(debug_assertions)))]
#[bench]
fn bench_encode_md5_password(b: &mut test::Bencher) {
use test::black_box;
let mut buf = Vec::with_capacity(128);
b.iter(|| {
buf.clear();
black_box(Password::Md5 {
password: "password",
username: "root",
salt: [147, 24, 57, 152],
})
.encode_msg(&mut buf);
});
}
}