use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use thiserror::Error;
const DEFAULT_LIFETIME: u64 = 60 * 60 * 24 * 28 * 3;
const DEFAULT_LIFETIME_MARGIN: u64 = 60 * 60;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Lifetime {
not_before: u64,
not_after: u64,
}
impl Lifetime {
pub fn new(t: u64) -> Self {
let lifetime_margin: u64 = DEFAULT_LIFETIME_MARGIN;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs();
let not_before = now - lifetime_margin;
let not_after = now + t;
Self {
not_before,
not_after,
}
}
pub fn verify(&self) -> Result<(), LifetimeError> {
let is_valid = match SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_secs())
{
Ok(elapsed) => self.not_before < elapsed && elapsed < self.not_after,
Err(err) => return Err(LifetimeError::SystemTime(err)),
};
if is_valid {
Ok(())
} else {
Err(LifetimeError::InvalidLifetime)
}
}
pub fn verify_with_window(&self, window: Duration) -> Result<(), LifetimeError> {
let is_valid = match SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_secs())
{
Ok(elapsed) => {
let elapsed = elapsed + window.as_secs();
self.not_before < elapsed && elapsed < self.not_after
}
Err(err) => return Err(LifetimeError::SystemTime(err)),
};
if is_valid {
Ok(())
} else {
Err(LifetimeError::InvalidLifetime)
}
}
}
impl Default for Lifetime {
fn default() -> Self {
Lifetime::new(DEFAULT_LIFETIME)
}
}
impl Ord for Lifetime {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.not_after.cmp(&other.not_after)
}
}
impl PartialOrd for Lifetime {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(any(test, feature = "test_utils"))]
impl Lifetime {
pub fn from_range(not_before: u64, not_after: u64) -> Self {
Self {
not_before,
not_after,
}
}
}
#[derive(Debug, Error)]
pub enum LifetimeError {
#[error("lifetime of pre-key is not valid")]
InvalidLifetime,
#[error(transparent)]
SystemTime(std::time::SystemTimeError),
}
#[cfg(test)]
mod tests {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use super::Lifetime;
#[test]
fn verify() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs();
let lifetime = Lifetime::default();
assert!(lifetime.verify().is_ok());
let lifetime = Lifetime::new(60);
assert!(lifetime.verify().is_ok());
assert!(
lifetime
.verify_with_window(Duration::from_secs(120))
.is_err()
);
let lifetime = Lifetime::from_range(now - 60, now + 60);
assert!(lifetime.verify().is_ok());
let lifetime = Lifetime::from_range(now + 60, now + 60); assert!(lifetime.verify().is_err());
let lifetime = Lifetime::from_range(now - 120, now - 60); assert!(lifetime.verify().is_err());
}
}