e2e-protection 0.2.0

Pluggable End-to-End (E2E) protection profiles. First class support for AUTOSAR P11 (CAN) and P22 (CAN FD).
Documentation
/// E2E errors encountered during frame packing or validation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum E2eError {
    LengthError,
    CrcMismatch,
    DataIdMismatch,
    CounterError,
    Unsupported,
}

/// Result returned by `check()`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CheckInfo {
    pub counter: u32,
    pub ok: bool,
}

/// Counter evolution policy.
#[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); // first => 0 (if step=1)
                (p + step) & m
            }
        }
    }
}

/// E2E profile interface (P11/P22 implement this).
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>;
}

/// Session = profile + counter policy + last counter.
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 }
}