#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum E2eError {
Length,
Crc,
DataId,
Counter,
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 {
Rollover { bits: u8, step: u32 },
None,
}
impl CounterPolicy {
pub fn next(self, prev: Option<u32>) -> u32 {
match self {
CounterPolicy::None => prev.unwrap_or(0),
CounterPolicy::Rollover { bits, step } => {
if bits == 32 {
prev.unwrap_or(u32::MAX).wrapping_add(step)
} else {
let m = (1u32 << bits) - 1;
let p = prev.unwrap_or(m);
(p + step) & m
}
}
}
}
pub fn ok(self, prev: Option<u32>, next: u32) -> bool {
match self {
CounterPolicy::None => true,
CounterPolicy::Rollover { bits, step } => {
if bits == 32 {
prev.map(|p| p.wrapping_add(step) == next).unwrap_or(true)
} else {
let m = (1u32 << bits) - 1;
prev.map(|p| ((p + step) & m) == (next & m)).unwrap_or(true)
}
}
}
}
}
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> {
p: P,
pol: CounterPolicy,
last: Option<u32>,
}
impl<P: E2eProfile> E2eSession<P> {
pub fn new(p: P, pol: CounterPolicy) -> Self {
Self { p, pol, last: None }
}
pub fn wrap(&mut self, payload: &[u8]) -> Result<Vec<u8>, E2eError> {
let c = self.pol.next(self.last);
let f = self.p.protect(payload, c)?;
self.last = Some(c);
Ok(f)
}
pub fn unwrap<'a>(&mut self, frame: &'a [u8]) -> Result<&'a [u8], E2eError> {
let info = self.p.check(frame)?;
if !self.pol.ok(self.last, info.counter) {
return Err(E2eError::Counter);
}
self.last = Some(info.counter);
let n = self.p.payload_len(frame).ok_or(E2eError::Unsupported)?;
Ok(&frame[..n])
}
pub fn last_counter(&self) -> Option<u32> { self.last }
}