use std::fmt::Write;
use md5::{Digest, Md5};
use crate::io::PgBufMutExt;
use crate::io::{BufMutExt, Encode};
#[derive(Debug)]
pub enum Password<'a> {
Cleartext(&'a str),
Md5 {
password: &'a str,
username: &'a str,
salt: [u8; 4],
},
}
impl Password<'_> {
#[inline]
fn len(&self) -> usize {
match self {
Password::Cleartext(s) => s.len() + 5,
Password::Md5 { .. } => 35 + 5,
}
}
}
impl Encode<'_> for Password<'_> {
fn encode_with(&self, buf: &mut Vec<u8>, _: ()) {
buf.reserve(1 + 4 + self.len());
buf.push(b'p');
buf.put_length_prefixed(|buf| {
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);
}
}
});
}
}
#[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(&mut buf);
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(&mut buf);
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(&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(&mut buf);
});
}