diff --git a/src/security/pairing.rs b/src/security/pairing.rs index d7cb0e5..c0ce018 100644 --- a/src/security/pairing.rs +++ b/src/security/pairing.rs @@ -28,7 +28,7 @@ pub struct PairingGuard { /// Whether pairing is required at all. require_pairing: bool, /// One-time pairing code (generated on startup, consumed on first pair). - pairing_code: Option, + pairing_code: Mutex>, /// Set of SHA-256 hashed bearer tokens (persisted across restarts). paired_tokens: Mutex>, /// Brute-force protection: failed attempt counter + lockout time. @@ -62,15 +62,18 @@ impl PairingGuard { }; Self { require_pairing, - pairing_code: code, + pairing_code: Mutex::new(code), paired_tokens: Mutex::new(tokens), failed_attempts: Mutex::new((0, None)), } } /// The one-time pairing code (only set when no tokens exist yet). - pub fn pairing_code(&self) -> Option<&str> { - self.pairing_code.as_deref() + pub fn pairing_code(&self) -> Option { + self.pairing_code + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() } /// Whether pairing is required at all. @@ -97,23 +100,33 @@ impl PairingGuard { } } - if let Some(ref expected) = self.pairing_code { - if constant_time_eq(code.trim(), expected.trim()) { - // Reset failed attempts on success - { - let mut attempts = self - .failed_attempts + { + let mut pairing_code = self + .pairing_code + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(ref expected) = *pairing_code { + if constant_time_eq(code.trim(), expected.trim()) { + // Reset failed attempts on success + { + let mut attempts = self + .failed_attempts + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *attempts = (0, None); + } + let token = generate_token(); + let mut tokens = self + .paired_tokens .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - *attempts = (0, None); + tokens.insert(hash_token(&token)); + + // Consume the pairing code so it cannot be reused + *pairing_code = None; + + return Ok(Some(token)); } - let token = generate_token(); - let mut tokens = self - .paired_tokens - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - tokens.insert(hash_token(&token)); - return Ok(Some(token)); } }