1use subtle::ConstantTimeEq as _;
4
5#[must_use]
7pub fn md5_response(username: &[u8], password: &[u8], salt: [u8; 4]) -> String {
8 postgres_protocol::authentication::md5_hash(username, password, salt)
9}
10
11#[must_use]
13pub fn verify_md5_response(
14 response: &[u8],
15 username: &[u8],
16 password: &[u8],
17 salt: [u8; 4],
18) -> bool {
19 response
20 .ct_eq(md5_response(username, password, salt).as_bytes())
21 .into()
22}
23
24#[must_use]
26pub fn verify_cleartext(response: &[u8], expected: &[u8]) -> bool {
27 response.ct_eq(expected).into()
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33
34 #[test]
35 fn computes_and_verifies_postgres_md5_response() {
36 let salt = [0x2a, 0x3d, 0x8f, 0xe0];
37 let response = md5_response(b"md5_user", b"password", salt);
38 assert_eq!(response, "md562af4dd09bbb41884907a838a3233294");
39 assert!(verify_md5_response(
40 response.as_bytes(),
41 b"md5_user",
42 b"password",
43 salt
44 ));
45 assert!(!verify_md5_response(
46 b"md500000000000000000000000000000000",
47 b"md5_user",
48 b"password",
49 salt
50 ));
51 }
52
53 #[test]
54 fn verifies_cleartext_credentials() {
55 assert!(verify_cleartext(b"secret", b"secret"));
56 assert!(!verify_cleartext(b"secret", b"different"));
57 }
58}