sqlx_core_oldapi/postgres/message/
password.rs

1use std::fmt::Write;
2
3use md5::{Digest, Md5};
4
5use crate::io::{BufMutExt, Encode};
6use crate::postgres::io::PgBufMutExt;
7
8#[derive(Debug)]
9pub enum Password<'a> {
10    Cleartext(&'a str),
11
12    Md5 {
13        password: &'a str,
14        username: &'a str,
15        salt: [u8; 4],
16    },
17}
18
19impl Password<'_> {
20    #[inline]
21    fn len(&self) -> usize {
22        match self {
23            Password::Cleartext(s) => s.len() + 5,
24            Password::Md5 { .. } => 35 + 5,
25        }
26    }
27}
28
29impl Encode<'_> for Password<'_> {
30    fn encode_with(&self, buf: &mut Vec<u8>, _: ()) {
31        buf.reserve(1 + 4 + self.len());
32        buf.push(b'p');
33
34        buf.put_length_prefixed(|buf| {
35            match self {
36                Password::Cleartext(password) => {
37                    buf.put_str_nul(password);
38                }
39
40                Password::Md5 {
41                    username,
42                    password,
43                    salt,
44                } => {
45                    // The actual `PasswordMessage` can be computed in SQL as
46                    // `concat('md5', md5(concat(md5(concat(password, username)), random-salt)))`.
47
48                    // Keep in mind the md5() function returns its result as a hex string.
49
50                    let mut hasher = Md5::new();
51
52                    hasher.update(password);
53                    hasher.update(username);
54
55                    let mut output = String::with_capacity(35);
56
57                    let _ = write!(output, "{:x}", hasher.finalize_reset());
58
59                    hasher.update(&output);
60                    hasher.update(salt);
61
62                    output.clear();
63
64                    let _ = write!(output, "md5{:x}", hasher.finalize());
65
66                    buf.put_str_nul(&output);
67                }
68            }
69        });
70    }
71}
72
73#[test]
74fn test_encode_clear_password() {
75    const EXPECTED: &[u8] = b"p\0\0\0\rpassword\0";
76
77    let mut buf = Vec::new();
78    let m = Password::Cleartext("password");
79
80    m.encode(&mut buf);
81
82    assert_eq!(buf, EXPECTED);
83}
84
85#[test]
86fn test_encode_md5_password() {
87    const EXPECTED: &[u8] = b"p\0\0\0(md53e2c9d99d49b201ef867a36f3f9ed62c\0";
88
89    let mut buf = Vec::new();
90    let m = Password::Md5 {
91        password: "password",
92        username: "root",
93        salt: [147, 24, 57, 152],
94    };
95
96    m.encode(&mut buf);
97
98    assert_eq!(buf, EXPECTED);
99}
100
101#[cfg(all(test, not(debug_assertions)))]
102#[bench]
103fn bench_encode_clear_password(b: &mut test::Bencher) {
104    use test::black_box;
105
106    let mut buf = Vec::with_capacity(128);
107
108    b.iter(|| {
109        buf.clear();
110
111        black_box(Password::Cleartext("password")).encode(&mut buf);
112    });
113}
114
115#[cfg(all(test, not(debug_assertions)))]
116#[bench]
117fn bench_encode_md5_password(b: &mut test::Bencher) {
118    use test::black_box;
119
120    let mut buf = Vec::with_capacity(128);
121
122    b.iter(|| {
123        buf.clear();
124
125        black_box(Password::Md5 {
126            password: "password",
127            username: "root",
128            salt: [147, 24, 57, 152],
129        })
130        .encode(&mut buf);
131    });
132}