1fn constant_time_eq(a: &str, b: &str) -> bool {
8 if a.len() != b.len() {
9 return false;
10 }
11 a.bytes()
12 .zip(b.bytes())
13 .fold(0, |acc, (x, y)| acc | (x ^ y))
14 == 0
15}
16
17pub struct BearerAuth {
19 token: String,
20}
21
22impl std::fmt::Debug for BearerAuth {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.debug_struct("BearerAuth")
26 .field("token", &"<redacted>")
27 .finish()
28 }
29}
30
31impl BearerAuth {
32 pub fn new(token: impl Into<String>) -> Self {
34 Self {
35 token: token.into(),
36 }
37 }
38
39 pub fn generate() -> Self {
51 Self::try_generate().expect("OS entropy source unavailable")
52 }
53
54 pub fn try_generate() -> crate::error::Result<Self> {
56 let mut bytes = [0u8; 16];
57 getrandom::fill(&mut bytes).map_err(|e| {
58 crate::error::KernelError::Config(format!("failed to read OS entropy for token: {e}"))
59 })?;
60 let mut token = String::with_capacity(32);
61 for b in bytes {
62 use std::fmt::Write;
63 let _ = write!(token, "{b:02x}");
64 }
65 Ok(Self { token })
66 }
67
68 pub fn validate(&self, header_value: &str) -> bool {
70 if let Some(token) = header_value.strip_prefix("Bearer ") {
71 constant_time_eq(token.trim(), &self.token)
72 } else {
73 false
74 }
75 }
76
77 pub fn token(&self) -> &str {
79 &self.token
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn validate_correct_token() {
89 let auth = BearerAuth::new("my-secret-token");
90 assert!(auth.validate("Bearer my-secret-token"));
91 }
92
93 #[test]
94 fn reject_wrong_token() {
95 let auth = BearerAuth::new("correct");
96 assert!(!auth.validate("Bearer wrong"));
97 }
98
99 #[test]
100 fn reject_missing_prefix() {
101 let auth = BearerAuth::new("token");
102 assert!(!auth.validate("token"));
103 assert!(!auth.validate("Basic token"));
104 }
105
106 #[test]
107 fn generate_produces_32_char_hex() {
108 let auth = BearerAuth::generate();
109 let token = auth.token();
110 assert_eq!(token.len(), 32);
111 assert!(token.chars().all(|c| c.is_ascii_hexdigit()));
112 }
113
114 #[test]
115 fn generate_unique() {
116 let a = BearerAuth::generate();
117 let b = BearerAuth::generate();
118 assert_ne!(a.token(), b.token());
119 }
120
121 #[test]
122 fn debug_never_prints_token() {
123 let auth = BearerAuth::new("super-secret-token");
124 let dbg = format!("{auth:?}");
125 assert!(!dbg.contains("super-secret-token"), "{dbg}");
126 }
127}