sqlx_postgres/message/
password.rs1use crate::io::BufMutExt;
2use crate::message::{FrontendMessage, FrontendMessageFormat};
3use md5::{Digest, Md5};
4use sqlx_core::Error;
5use std::num::Saturating;
6
7#[derive(Debug)]
8pub enum Password<'a> {
9 Cleartext(&'a str),
10
11 Md5 {
12 password: &'a str,
13 username: &'a str,
14 salt: [u8; 4],
15 },
16}
17
18impl FrontendMessage for Password<'_> {
19 const FORMAT: FrontendMessageFormat = FrontendMessageFormat::PasswordPolymorphic;
20
21 #[inline(always)]
22 fn body_size_hint(&self) -> Saturating<usize> {
23 let mut size = Saturating(0);
24
25 match self {
26 Password::Cleartext(password) => {
27 size += password
32 .len()
33 .saturating_add(1) .checked_next_power_of_two()
35 .unwrap_or(usize::MAX);
36 }
37 Password::Md5 { .. } => {
38 size += 36;
40 }
41 }
42
43 size
44 }
45
46 fn encode_body(&self, buf: &mut Vec<u8>) -> Result<(), Error> {
47 match self {
48 Password::Cleartext(password) => {
49 buf.put_str_nul(password);
50 }
51
52 Password::Md5 {
53 username,
54 password,
55 salt,
56 } => {
57 let mut hasher = Md5::new();
63
64 hasher.update(password);
65 hasher.update(username);
66
67 let mut output = hex::encode(hasher.finalize_reset());
68
69 hasher.update(&output);
70 hasher.update(salt);
71
72 output = hex::encode(hasher.finalize());
75 output.insert_str(0, "md5");
76
77 buf.put_str_nul(&output);
78 }
79 }
80
81 Ok(())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use crate::message::FrontendMessage;
88
89 use super::Password;
90
91 #[test]
92 fn test_encode_clear_password() {
93 const EXPECTED: &[u8] = b"p\0\0\0\rpassword\0";
94
95 let mut buf = Vec::new();
96 let m = Password::Cleartext("password");
97
98 m.encode_msg(&mut buf).unwrap();
99
100 assert_eq!(buf, EXPECTED);
101 }
102
103 #[test]
104 fn test_encode_md5_password() {
105 const EXPECTED: &[u8] = b"p\0\0\0(md53e2c9d99d49b201ef867a36f3f9ed62c\0";
106
107 let mut buf = Vec::new();
108 let m = Password::Md5 {
109 password: "password",
110 username: "root",
111 salt: [147, 24, 57, 152],
112 };
113
114 m.encode_msg(&mut buf).unwrap();
115
116 assert_eq!(buf, EXPECTED);
117 }
118
119 #[cfg(all(test, not(debug_assertions)))]
120 #[bench]
121 fn bench_encode_clear_password(b: &mut test::Bencher) {
122 use test::black_box;
123
124 let mut buf = Vec::with_capacity(128);
125
126 b.iter(|| {
127 buf.clear();
128
129 black_box(Password::Cleartext("password")).encode_msg(&mut buf);
130 });
131 }
132
133 #[cfg(all(test, not(debug_assertions)))]
134 #[bench]
135 fn bench_encode_md5_password(b: &mut test::Bencher) {
136 use test::black_box;
137
138 let mut buf = Vec::with_capacity(128);
139
140 b.iter(|| {
141 buf.clear();
142
143 black_box(Password::Md5 {
144 password: "password",
145 username: "root",
146 salt: [147, 24, 57, 152],
147 })
148 .encode_msg(&mut buf);
149 });
150 }
151}