use crc::{Crc, CRC_64_ECMA_182};
use crate::e2e::{CheckInfo, E2eError, E2eProfile};
const CRC64_ECMA: Crc<u64> = Crc::<u64>::new(&CRC_64_ECMA_182);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct P7 {
pub data_id: u32,
}
impl E2eProfile for P7 {
fn protect(&self, payload: &[u8], counter: u32) -> Result<Vec<u8>, E2eError> {
let mut out = Vec::with_capacity(payload.len() + 20);
out.extend_from_slice(payload);
out.extend_from_slice(&(payload.len() as u32).to_be_bytes());
out.extend_from_slice(&counter.to_be_bytes());
out.extend_from_slice(&self.data_id.to_be_bytes());
let crc = CRC64_ECMA.checksum(&out);
out.extend_from_slice(&crc.to_be_bytes());
Ok(out)
}
fn check(&self, frame: &[u8]) -> Result<CheckInfo, E2eError> {
if frame.len() < 20 {
return Err(E2eError::Length);
}
let crc_recv = u64::from_be_bytes(frame[frame.len() - 8..].try_into().unwrap());
let without_crc = &frame[..frame.len() - 8];
let want = CRC64_ECMA.checksum(without_crc);
if want != crc_recv {
return Err(E2eError::Crc);
}
let tail = &without_crc[without_crc.len() - 12..];
let len = u32::from_be_bytes(tail[0..4].try_into().unwrap()) as usize;
let counter = u32::from_be_bytes(tail[4..8].try_into().unwrap());
let data_id = u32::from_be_bytes(tail[8..12].try_into().unwrap());
if data_id != self.data_id {
return Err(E2eError::DataId);
}
let payload_len = without_crc.len() - 12;
if len != payload_len {
return Err(E2eError::Length);
}
Ok(CheckInfo { counter, ok: true })
}
fn payload_len(&self, frame: &[u8]) -> Option<usize> {
frame.len().checked_sub(20)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CounterPolicy, E2eProfile, E2eSession, E2eError};
#[test]
fn p7_roundtrip() {
let mut tx =
E2eSession::new(P7 { data_id: 0xCAFEBABE }, CounterPolicy::Rollover { bits: 32, step: 1 });
let f = tx.wrap(&[0xAA, 0xBB, 0xCC]).unwrap();
let mut rx =
E2eSession::new(P7 { data_id: 0xCAFEBABE }, CounterPolicy::Rollover { bits: 32, step: 1 });
let pl = rx.unwrap(&f).unwrap();
assert_eq!(pl, &[0xAA, 0xBB, 0xCC]);
}
#[test]
fn p7_crc_detects_error() {
let p = P7 { data_id: 0x42 };
let mut f = p.protect(&[1, 2, 3, 4], 55).unwrap();
f[1] ^= 0xFF;
assert!(matches!(p.check(&f), Err(E2eError::Crc)));
}
}