use super::config::{Profile4Config, Profile5Config};
use super::crc::{compute_crc16_p5, compute_crc16_p5_with_header, compute_crc32_p4};
use super::e2e_protector::{PROFILE4_HEADER_SIZE, PROFILE5_HEADER_SIZE};
use super::state::{Profile4State, Profile5State};
use super::{E2ECheckResult, E2ECheckStatus};
pub fn check_profile4<'a>(
config: &Profile4Config,
state: &mut Profile4State,
protected: &'a [u8],
) -> E2ECheckResult<'a> {
if protected.len() < PROFILE4_HEADER_SIZE {
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let length = u16::from_be_bytes([protected[0], protected[1]]);
let counter = u16::from_be_bytes([protected[2], protected[3]]);
let data_id = u32::from_be_bytes([protected[4], protected[5], protected[6], protected[7]]);
let received_crc =
u32::from_be_bytes([protected[8], protected[9], protected[10], protected[11]]);
if length as usize != protected.len() {
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
if data_id != config.data_id {
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let payload = &protected[PROFILE4_HEADER_SIZE..];
let computed_crc = compute_crc32_p4(length, counter, data_id, payload);
if computed_crc != received_crc {
return E2ECheckResult::error(E2ECheckStatus::CrcError);
}
let status = check_sequence_profile4(state, counter, config.max_delta_counter);
state.last_counter = Some(counter);
E2ECheckResult::success(status, u32::from(counter), payload)
}
pub fn check_profile5<'a>(
config: &Profile5Config,
state: &mut Profile5State,
protected: &'a [u8],
) -> E2ECheckResult<'a> {
if protected.len() < PROFILE5_HEADER_SIZE {
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let expected_total_length = PROFILE5_HEADER_SIZE + config.data_length as usize;
if protected.len() != expected_total_length {
crate::log::warn!(
"E2E Profile 5 length mismatch: expected {} bytes (3 header + {} payload), got {} bytes",
expected_total_length,
config.data_length,
protected.len()
);
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let received_crc = u16::from_le_bytes([protected[0], protected[1]]);
let counter = protected[2];
let payload = &protected[PROFILE5_HEADER_SIZE..];
let computed_crc = compute_crc16_p5(config.data_id, counter, payload);
if computed_crc != received_crc {
return E2ECheckResult::error(E2ECheckStatus::CrcError);
}
let status = check_sequence_profile5(state, counter, config.max_delta_counter);
state.last_counter = Some(counter);
E2ECheckResult::success(status, u32::from(counter), payload)
}
pub fn check_profile5_with_header<'a>(
config: &Profile5Config,
state: &mut Profile5State,
protected: &'a [u8],
upper_header: [u8; 8],
) -> E2ECheckResult<'a> {
if protected.len() < PROFILE5_HEADER_SIZE {
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let expected_total_length = PROFILE5_HEADER_SIZE + config.data_length as usize;
if protected.len() != expected_total_length {
crate::log::warn!(
"E2E Profile 5 length mismatch: expected {} bytes (3 header + {} payload), got {} bytes",
expected_total_length,
config.data_length,
protected.len()
);
return E2ECheckResult::error(E2ECheckStatus::BadArgument);
}
let received_crc = u16::from_le_bytes([protected[0], protected[1]]);
let counter = protected[2];
let payload = &protected[PROFILE5_HEADER_SIZE..];
let computed_crc = compute_crc16_p5_with_header(config.data_id, counter, payload, upper_header);
if computed_crc != received_crc {
return E2ECheckResult::error(E2ECheckStatus::CrcError);
}
let status = check_sequence_profile5(state, counter, config.max_delta_counter);
state.last_counter = Some(counter);
E2ECheckResult::success(status, u32::from(counter), payload)
}
fn check_sequence_profile4(
state: &Profile4State,
received_counter: u16,
max_delta: u16,
) -> E2ECheckStatus {
match state.last_counter {
None => {
E2ECheckStatus::Ok
}
Some(last_counter) => {
let delta = received_counter.wrapping_sub(last_counter);
if delta == 0 {
E2ECheckStatus::Repeated
} else if delta == 1 {
E2ECheckStatus::Ok
} else if delta <= max_delta {
E2ECheckStatus::OkSomeLost
} else {
E2ECheckStatus::WrongSequence
}
}
}
}
fn check_sequence_profile5(
state: &Profile5State,
received_counter: u8,
max_delta: u8,
) -> E2ECheckStatus {
match state.last_counter {
None => {
E2ECheckStatus::Ok
}
Some(last_counter) => {
let delta = received_counter.wrapping_sub(last_counter);
if delta == 0 {
E2ECheckStatus::Repeated
} else if delta == 1 {
E2ECheckStatus::Ok
} else if delta <= max_delta {
E2ECheckStatus::OkSomeLost
} else {
E2ECheckStatus::WrongSequence
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::e2e::{protect_profile4, protect_profile5, protect_profile5_with_header};
#[test]
fn test_check_profile4_valid() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"Hello, World!";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let protected = &buf[..len];
let result = check_profile4(&config, &mut check_state, protected);
assert_eq!(result.status, E2ECheckStatus::Ok);
assert_eq!(result.counter, Some(0));
assert_eq!(result.payload, Some(payload.as_slice()));
}
#[test]
fn test_check_profile4_wrong_data_id() {
let config1 = Profile4Config::new(0x1234_5678, 15);
let config2 = Profile4Config::new(0xDEAD_BEEF, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config1, &mut protect_state, payload, &mut buf).unwrap();
let result = check_profile4(&config2, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_check_profile4_corrupted_crc() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
buf[8] ^= 0xFF;
let result = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::CrcError);
}
#[test]
fn test_check_profile4_corrupted_payload() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
buf[12] ^= 0xFF;
let result = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::CrcError);
}
#[test]
fn test_check_profile4_wrong_length() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result = check_profile4(&config, &mut check_state, &buf[..14]);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_check_profile4_too_short() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut check_state = Profile4State::new();
let short = [0u8; 11]; let result = check_profile4(&config, &mut check_state, &short);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_check_profile5_valid() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..13].copy_from_slice(b"Hello, World!");
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let protected = &buf[..len];
let result = check_profile5(&config, &mut check_state, protected);
assert_eq!(result.status, E2ECheckStatus::Ok);
assert_eq!(result.counter, Some(0));
assert_eq!(result.payload, Some(payload.as_slice()));
}
#[test]
fn test_check_profile5_corrupted_crc() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..4].copy_from_slice(b"test");
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
buf[1] ^= 0xFF;
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::CrcError);
}
#[test]
fn test_check_profile5_too_short() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut check_state = Profile5State::new();
let short = [0u8; 2]; let result = check_profile5(&config, &mut check_state, &short);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_sequence_repeated() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let protected = &buf[..len];
let result1 = check_profile4(&config, &mut check_state, protected);
assert_eq!(result1.status, E2ECheckStatus::Ok);
let result2 = check_profile4(&config, &mut check_state, protected);
assert_eq!(result2.status, E2ECheckStatus::Repeated);
}
#[test]
fn test_sequence_consecutive() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
for _ in 0..5 {
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
}
}
#[test]
fn test_sequence_some_lost() {
let config = Profile4Config::new(0x1234_5678, 10);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result1.status, E2ECheckStatus::Ok);
for _ in 0..5 {
protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
}
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result2 = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result2.status, E2ECheckStatus::OkSomeLost);
}
#[test]
fn test_sequence_wrong_sequence() {
let config = Profile4Config::new(0x1234_5678, 3);
let mut protect_state = Profile4State::new();
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result1.status, E2ECheckStatus::Ok);
for _ in 0..10 {
protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
}
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result2 = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result2.status, E2ECheckStatus::WrongSequence);
}
#[test]
fn test_sequence_wraparound() {
let config = Profile4Config::new(0x1234_5678, 5);
let mut protect_state = Profile4State::with_initial_counter(u16::MAX - 2);
let mut check_state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
for _ in 0..5 {
let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
let result = check_profile4(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
}
}
#[test]
fn test_profile5_sequence_wraparound() {
let config = Profile5Config::new(0x1234, 20, 5);
let mut protect_state = Profile5State::with_initial_counter(u8::MAX - 2);
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..4].copy_from_slice(b"test");
let mut buf = [0u8; 256];
for _ in 0..5 {
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
}
}
#[test]
fn test_check_profile5_with_header_roundtrip() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut payload = [0u8; 20];
payload[..5].copy_from_slice(b"Hello");
let mut buf = [0u8; 256];
let len = protect_profile5_with_header(
&config,
&mut protect_state,
&payload,
upper_header,
&mut buf,
)
.unwrap();
let result =
check_profile5_with_header(&config, &mut check_state, &buf[..len], upper_header);
assert_eq!(result.status, E2ECheckStatus::Ok);
assert_eq!(result.counter, Some(0));
assert_eq!(result.payload, Some(payload.as_slice()));
}
#[test]
fn test_check_profile5_length_mismatch() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut check_state = Profile5State::new();
let buf = [0u8; 10]; let result = check_profile5(&config, &mut check_state, &buf);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_check_profile5_with_header_too_short() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut check_state = Profile5State::new();
let upper_header: [u8; 8] = [0; 8];
let buf = [0u8; 2]; let result = check_profile5_with_header(&config, &mut check_state, &buf, upper_header);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_check_profile5_with_header_length_mismatch() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut check_state = Profile5State::new();
let upper_header: [u8; 8] = [0; 8];
let buf = [0u8; 10]; let result = check_profile5_with_header(&config, &mut check_state, &buf, upper_header);
assert_eq!(result.status, E2ECheckStatus::BadArgument);
}
#[test]
fn test_profile5_sequence_some_lost() {
let config = Profile5Config::new(0x1234, 20, 10);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..4].copy_from_slice(b"test");
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
for _ in 0..5 {
protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
}
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::OkSomeLost);
}
#[test]
fn test_profile5_sequence_wrong_sequence() {
let config = Profile5Config::new(0x1234, 20, 3);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..4].copy_from_slice(b"test");
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
for _ in 0..10 {
protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
}
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::WrongSequence);
}
#[test]
fn test_profile5_sequence_repeated() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let mut payload = [0u8; 20];
payload[..4].copy_from_slice(b"test");
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Ok);
let result = check_profile5(&config, &mut check_state, &buf[..len]);
assert_eq!(result.status, E2ECheckStatus::Repeated);
}
#[test]
fn test_check_profile5_with_header_mismatch_is_crc_error() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut protect_state = Profile5State::new();
let mut check_state = Profile5State::new();
let tx_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let rx_header: [u8; 8] = [0x00, 0x02, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut payload = [0u8; 20];
payload[..5].copy_from_slice(b"Hello");
let mut buf = [0u8; 256];
let len = protect_profile5_with_header(
&config,
&mut protect_state,
&payload,
tx_header,
&mut buf,
)
.unwrap();
let result = check_profile5_with_header(&config, &mut check_state, &buf[..len], rx_header);
assert_eq!(result.status, E2ECheckStatus::CrcError);
}
}