//! Shared HTTP-daemon helpers for the operator tools (Sando, Bento). //! //! Both daemons expose a small axum service guarded by a single bearer token, //! and both compared that token the same way — a constant-time byte compare, to //! the byte, test included. That one belongs here so there is exactly one copy. //! //! Not everything the two daemons appear to share actually is: `require_bearer` //! diverged (Sando attributes each authenticated POST to its peer; Bento leaves //! reads open and logs nothing), and the `Error` → response mappers carry //! different variants and leak postures (Bento answers a leak-safe JSON //! envelope; Sando returns `to_string()` text). Folding those is a behavior //! change, not a mechanical dedup, so each daemon keeps its own — only the //! genuinely identical primitive lives here. /// Constant-time string compare for bearer tokens. The length may leak (a /// token's length is not the secret); the byte comparison does not /// short-circuit, so a matching prefix is not distinguishable by timing. #[must_use] pub fn ct_eq(a: &str, b: &str) -> bool { let (a, b) = (a.as_bytes(), b.as_bytes()); if a.len() != b.len() { return false; } let mut diff = 0u8; for (x, y) in a.iter().zip(b) { diff |= x ^ y; } diff == 0 } #[cfg(test)] mod tests { use super::*; #[test] fn ct_eq_matches_only_identical_strings() { assert!(ct_eq("abc", "abc")); assert!(!ct_eq("abc", "abd")); assert!(!ct_eq("abc", "ab")); assert!(!ct_eq("", "x")); assert!(ct_eq("", "")); } }