1#[inline]
12pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
13 if a.len() != b.len() {
14 return false;
15 }
16 let mut diff = 0u8;
17 for (x, y) in a.iter().zip(b.iter()) {
18 diff |= x ^ y;
19 }
20 diff == 0
21}
22
23#[inline]
25pub fn verify_token(configured: &str, presented: &str) -> bool {
26 constant_time_eq(configured.as_bytes(), presented.as_bytes())
27}
28
29pub fn is_loopback_host(host: &str) -> bool {
34 if host.eq_ignore_ascii_case("localhost") {
35 return true;
36 }
37 host.parse::<std::net::IpAddr>()
38 .map(|ip| ip.is_loopback())
39 .unwrap_or(false)
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn test_constant_time_eq() {
48 assert!(constant_time_eq(b"secret", b"secret"));
49 assert!(!constant_time_eq(b"secret", b"secreu"));
50 assert!(!constant_time_eq(b"secret", b"secr"));
51 assert!(constant_time_eq(b"", b""));
52 }
53
54 #[test]
55 fn test_verify_token() {
56 assert!(verify_token("hunter2", "hunter2"));
57 assert!(!verify_token("hunter2", "Hunter2"));
58 assert!(!verify_token("hunter2", ""));
59 }
60
61 #[test]
62 fn test_is_loopback_host() {
63 assert!(is_loopback_host("127.0.0.1"));
64 assert!(is_loopback_host("::1"));
65 assert!(is_loopback_host("localhost"));
66 assert!(is_loopback_host("LOCALHOST"));
67 assert!(!is_loopback_host("0.0.0.0"));
68 assert!(!is_loopback_host("::"));
69 assert!(!is_loopback_host("192.168.1.10"));
70 assert!(!is_loopback_host("example.com"));
71 }
72}