//! # Basic state manager //! //! The state manager included in this module can be used to //! generate basic and stateless states for applications with //! minimum security requirements. The states contains the IP //! address of the client in an encrypted way, and expires 15 //! minutes after issuance. use std::error::Error; use std::fmt; use crate::crypto_wrapper::CryptoWrapper; use crate::time_utils::time; use bincode::{Decode, Encode}; use std::net::IpAddr; #[derive(Encode, Decode, Debug)] struct State { ip: IpAddr, expire: u64, } impl State { pub fn new(ip: IpAddr) -> Self { Self { ip, expire: time() + 15 * 60, } } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] enum StateError { InvalidIp, Expired, } impl Error for StateError {} impl fmt::Display for StateError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "StateManager error {:?}", self) } } /// Basic state manager. Can be used to prevent CRSF by encrypting /// a token containing a lifetime and the IP address of the user pub struct BasicStateManager(CryptoWrapper); impl BasicStateManager { /// Initialize the state manager by creating a random encryption key. This function /// should be called only one, ideally in the main function of the application pub fn new() -> Self { Self(CryptoWrapper::new_random()) } /// Initialize state manager with a given CryptoWrapper pub fn new_with_wrapper(wrapper: CryptoWrapper) -> Self { Self(wrapper) } /// Generate a new state pub fn gen_state(&self, ip: IpAddr) -> Result> { let state = State::new(ip); self.0.encrypt(&state) } /// Validate given state on callback URL pub fn validate_state(&self, ip: IpAddr, state: &str) -> Result<(), Box> { let state: State = self.0.decrypt(state)?; if state.ip != ip { return Err(Box::new(StateError::InvalidIp)); } if state.expire < time() { return Err(Box::new(StateError::Expired)); } Ok(()) } } impl Default for BasicStateManager { fn default() -> Self { Self::new() } } #[cfg(test)] mod test { use crate::basic_state_manager::BasicStateManager; use std::net::{IpAddr, Ipv4Addr}; const IP_1: IpAddr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); const IP_2: IpAddr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)); #[test] fn valid_state() { let manager = BasicStateManager::new(); let state = manager.gen_state(IP_1).unwrap(); assert!(manager.validate_state(IP_1, &state).is_ok()); } #[test] fn invalid_ip() { let manager = BasicStateManager::new(); let state = manager.gen_state(IP_1).unwrap(); assert!(manager.validate_state(IP_2, &state).is_err()); } }