#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum E2eError {
LengthError,
CrcMismatch,
DataIdMismatch,
CounterError,
Unsupported,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CheckInfo {
pub counter: u32,
pub ok: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CounterPolicy {
MonotonicRollover { bits: u8, step: u32 },
None,
}
impl CounterPolicy {
pub fn is_valid_successor(self, prev: Option<u32>, next: u32) -> bool {
match self {
CounterPolicy::None => true,
CounterPolicy::MonotonicRollover { bits, step } => {
let m = (1u32 << bits) - 1;
match prev { None => true, Some(p) => ((p + step) & m) == (next & m) }
}
}
}
pub fn next(self, prev: Option<u32>) -> u32 {
match self {
CounterPolicy::None => prev.unwrap_or(0),
CounterPolicy::MonotonicRollover { bits, step } => {
let m = (1u32 << bits) - 1;
let p = prev.unwrap_or(m); (p + step) & m
}
}
}
}
pub trait E2eProfile {
fn protect(&self, payload: &[u8], counter: u32) -> Result<Vec<u8>, E2eError>;
fn check(&self, frame: &[u8]) -> Result<CheckInfo, E2eError>;
fn payload_len(&self, frame: &[u8]) -> Option<usize>;
}
pub struct E2eSession<P: E2eProfile> {
profile: P,
policy: CounterPolicy,
last_counter: Option<u32>,
}
impl<P: E2eProfile> E2eSession<P> {
pub fn new(profile: P, policy: CounterPolicy) -> Self {
Self { profile, policy, last_counter: None }
}
pub fn wrap(&mut self, payload: &[u8]) -> Result<Vec<u8>, E2eError> {
let next = self.policy.next(self.last_counter);
let frame = self.profile.protect(payload, next)?;
self.last_counter = Some(next);
Ok(frame)
}
pub fn unwrap<'a>(&mut self, frame: &'a [u8]) -> Result<&'a [u8], E2eError> {
let info = self.profile.check(frame)?;
if !self.policy.is_valid_successor(self.last_counter, info.counter) {
return Err(E2eError::CounterError);
}
self.last_counter = Some(info.counter);
let plen = self.profile.payload_len(frame).ok_or(E2eError::Unsupported)?;
Ok(&frame[..plen])
}
pub fn last_counter(&self) -> Option<u32> { self.last_counter }
pub fn profile(&self) -> &P { &self.profile }
}