use crc::{Crc, CRC_32_ISCSI};
use std::{
fmt::{self, Debug, Formatter},
net::SocketAddrV4,
time::Instant,
};
use tracing::trace;
const SECRET_SIZE: usize = 20;
const TOKEN_SIZE: usize = 4;
const CASTAGNOLI: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
#[derive(Clone)]
pub struct Tokens {
prev_secret: [u8; SECRET_SIZE],
curr_secret: [u8; SECRET_SIZE],
last_updated: Instant,
}
impl Debug for Tokens {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Tokens (_)")
}
}
impl Tokens {
pub fn new() -> Self {
Tokens {
prev_secret: random(),
curr_secret: random(),
last_updated: Instant::now(),
}
}
pub fn should_update(&self) -> bool {
self.last_updated.elapsed() > crate::common::TOKEN_ROTATE_INTERVAL
}
pub fn validate(&mut self, address: SocketAddrV4, token: &[u8]) -> bool {
let prev = self.internal_generate_token(address, self.prev_secret);
let curr = self.internal_generate_token(address, self.curr_secret);
token == curr || token == prev
}
pub fn rotate(&mut self) {
trace!("Rotating secrets");
self.prev_secret = self.curr_secret;
self.curr_secret = random();
self.last_updated = Instant::now();
}
pub fn generate_token(&mut self, address: SocketAddrV4) -> [u8; 4] {
self.internal_generate_token(address, self.curr_secret)
}
fn internal_generate_token(
&mut self,
address: SocketAddrV4,
secret: [u8; SECRET_SIZE],
) -> [u8; TOKEN_SIZE] {
let mut digest = CASTAGNOLI.digest();
let octets: Box<[u8]> = address.ip().octets().into();
digest.update(&octets);
digest.update(&secret);
let checksum = digest.finalize();
checksum.to_be_bytes()
}
}
impl Default for Tokens {
fn default() -> Self {
Self::new()
}
}
fn random() -> [u8; SECRET_SIZE] {
let mut bytes = [0_u8; SECRET_SIZE];
getrandom::fill(&mut bytes).expect("getrandom");
bytes
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn valid_tokens() {
let mut tokens = Tokens::new();
let address = SocketAddrV4::new([127, 0, 0, 1].into(), 6881);
let token = tokens.generate_token(address);
assert!(tokens.validate(address, &token))
}
}