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