use super::Error;
use super::config::{Profile4Config, Profile5Config};
use super::crc::{compute_crc16_p5, compute_crc16_p5_with_header, compute_crc32_p4};
use super::state::{Profile4State, Profile5State};
pub const PROFILE4_HEADER_SIZE: usize = 12;
pub const PROFILE5_HEADER_SIZE: usize = 3;
pub fn protect_profile4(
config: &Profile4Config,
state: &mut Profile4State,
payload: &[u8],
output: &mut [u8],
) -> Result<usize, Error> {
let total_length = PROFILE4_HEADER_SIZE + payload.len();
if output.len() < total_length {
return Err(Error::BufferTooSmall {
needed: total_length,
actual: output.len(),
});
}
let length = u16::try_from(total_length).map_err(|_| Error::BufferTooSmall {
needed: total_length,
actual: output.len(),
})?;
let counter = state.protect_counter;
let crc = compute_crc32_p4(length, counter, config.data_id, payload);
output[0..2].copy_from_slice(&length.to_be_bytes());
output[2..4].copy_from_slice(&counter.to_be_bytes());
output[4..8].copy_from_slice(&config.data_id.to_be_bytes());
output[8..12].copy_from_slice(&crc.to_be_bytes());
output[PROFILE4_HEADER_SIZE..total_length].copy_from_slice(payload);
state.protect_counter = state.protect_counter.wrapping_add(1);
Ok(total_length)
}
pub fn protect_profile5(
config: &Profile5Config,
state: &mut Profile5State,
payload: &[u8],
output: &mut [u8],
) -> Result<usize, Error> {
let total_length = PROFILE5_HEADER_SIZE + payload.len();
if output.len() < total_length {
return Err(Error::BufferTooSmall {
needed: total_length,
actual: output.len(),
});
}
let counter = state.protect_counter;
let crc = compute_crc16_p5(config.data_id, counter, payload);
output[0..2].copy_from_slice(&crc.to_le_bytes());
output[2] = counter;
output[PROFILE5_HEADER_SIZE..total_length].copy_from_slice(payload);
state.protect_counter = state.protect_counter.wrapping_add(1);
Ok(total_length)
}
pub fn protect_profile5_with_header(
config: &Profile5Config,
state: &mut Profile5State,
payload: &[u8],
upper_header: [u8; 8],
output: &mut [u8],
) -> Result<usize, Error> {
let total_length = PROFILE5_HEADER_SIZE + payload.len();
if output.len() < total_length {
return Err(Error::BufferTooSmall {
needed: total_length,
actual: output.len(),
});
}
let counter = state.protect_counter;
let crc = compute_crc16_p5_with_header(config.data_id, counter, payload, upper_header);
output[0..2].copy_from_slice(&crc.to_le_bytes());
output[2] = counter;
output[PROFILE5_HEADER_SIZE..total_length].copy_from_slice(payload);
state.protect_counter = state.protect_counter.wrapping_add(1);
Ok(total_length)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_protect_profile4_header_format() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut state, payload, &mut buf).unwrap();
let protected = &buf[..len];
assert_eq!(len, 12 + 4);
let length = u16::from_be_bytes([protected[0], protected[1]]);
assert_eq!(length, 16);
let counter = u16::from_be_bytes([protected[2], protected[3]]);
assert_eq!(counter, 0);
let data_id = u32::from_be_bytes([protected[4], protected[5], protected[6], protected[7]]);
assert_eq!(data_id, 0x1234_5678);
assert_eq!(&protected[12..], b"test");
}
#[test]
fn test_protect_profile4_counter_increment() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 256];
for i in 0..5 {
let len = protect_profile4(&config, &mut state, payload, &mut buf).unwrap();
let counter = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(counter, i);
assert_eq!(len, 16);
}
}
#[test]
fn test_protect_profile4_counter_wraps() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::with_initial_counter(u16::MAX);
let payload = b"test";
let mut buf = [0u8; 256];
protect_profile4(&config, &mut state, payload, &mut buf).unwrap();
let counter1 = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(counter1, u16::MAX);
protect_profile4(&config, &mut state, payload, &mut buf).unwrap();
let counter2 = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(counter2, 0); }
#[test]
fn test_protect_profile5_header_format() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut state, payload, &mut buf).unwrap();
let protected = &buf[..len];
assert_eq!(len, 3 + 4);
assert_eq!(protected[2], 0);
assert_eq!(&protected[3..], b"test");
}
#[test]
fn test_protect_profile5_counter_increment() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let mut buf = [0u8; 256];
for i in 0..5u8 {
protect_profile5(&config, &mut state, payload, &mut buf).unwrap();
assert_eq!(buf[2], i); }
}
#[test]
fn test_protect_profile5_counter_wraps() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::with_initial_counter(u8::MAX);
let payload = b"test";
let mut buf = [0u8; 256];
protect_profile5(&config, &mut state, payload, &mut buf).unwrap();
assert_eq!(buf[2], u8::MAX);
protect_profile5(&config, &mut state, payload, &mut buf).unwrap();
assert_eq!(buf[2], 0); }
#[test]
fn test_protect_profile5_with_header_format() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf = [0u8; 256];
let len =
protect_profile5_with_header(&config, &mut state, payload, upper_header, &mut buf)
.unwrap();
let protected = &buf[..len];
assert_eq!(len, 3 + 4);
assert_eq!(protected[2], 0);
assert_eq!(&protected[3..], b"test");
}
#[test]
fn test_protect_profile5_with_header_counter_increment() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf = [0u8; 256];
for i in 0..5u8 {
protect_profile5_with_header(&config, &mut state, payload, upper_header, &mut buf)
.unwrap();
assert_eq!(buf[2], i);
}
}
#[test]
fn test_protect_profile5_with_header_counter_wraps() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::with_initial_counter(u8::MAX);
let payload = b"test";
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf = [0u8; 256];
protect_profile5_with_header(&config, &mut state, payload, upper_header, &mut buf).unwrap();
assert_eq!(buf[2], u8::MAX);
protect_profile5_with_header(&config, &mut state, payload, upper_header, &mut buf).unwrap();
assert_eq!(buf[2], 0); }
#[test]
fn test_protect_profile5_with_header_empty_payload() {
let config = Profile5Config::new(0x1234, 3, 15);
let mut state = Profile5State::new();
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf = [0u8; 256];
let len =
protect_profile5_with_header(&config, &mut state, b"", upper_header, &mut buf).unwrap();
assert_eq!(len, 3); }
#[test]
fn test_protect_profile5_with_header_differs_from_no_header() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state_a = Profile5State::new();
let mut state_b = Profile5State::new();
let payload = b"test";
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf_a = [0u8; 256];
let len_a = protect_profile5(&config, &mut state_a, payload, &mut buf_a).unwrap();
let without_header_crc = u16::from_le_bytes([buf_a[0], buf_a[1]]);
let mut buf_b = [0u8; 256];
let len_b =
protect_profile5_with_header(&config, &mut state_b, payload, upper_header, &mut buf_b)
.unwrap();
let with_header_crc = u16::from_le_bytes([buf_b[0], buf_b[1]]);
assert_eq!(buf_a[2], buf_b[2]); assert_eq!(&buf_a[3..len_a], &buf_b[3..len_b]); assert_ne!(without_header_crc, with_header_crc); }
#[test]
fn test_protect_profile4_buffer_too_small() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::new();
let payload = b"test";
let mut buf = [0u8; 10];
let err = protect_profile4(&config, &mut state, payload, &mut buf).unwrap_err();
assert!(matches!(
err,
crate::e2e::Error::BufferTooSmall {
needed: 16,
actual: 10,
}
));
}
#[test]
fn test_protect_profile5_buffer_too_small() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let mut buf = [0u8; 5];
let err = protect_profile5(&config, &mut state, payload, &mut buf).unwrap_err();
assert!(matches!(
err,
crate::e2e::Error::BufferTooSmall {
needed: 7,
actual: 5,
}
));
}
#[test]
fn test_protect_profile5_with_header_buffer_too_small() {
let config = Profile5Config::new(0x1234, 20, 15);
let mut state = Profile5State::new();
let payload = b"test";
let upper_header: [u8; 8] = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let mut buf = [0u8; 5];
let err =
protect_profile5_with_header(&config, &mut state, payload, upper_header, &mut buf)
.unwrap_err();
assert!(matches!(
err,
crate::e2e::Error::BufferTooSmall {
needed: 7,
actual: 5,
}
));
}
#[test]
#[cfg(feature = "std")]
fn test_protect_profile4_length_overflow() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::new();
let payload = std::vec![0u8; 65536];
let mut buf = std::vec![0u8; 65536 + 12];
let err = protect_profile4(&config, &mut state, &payload, &mut buf).unwrap_err();
assert!(matches!(err, crate::e2e::Error::BufferTooSmall { .. }));
}
#[test]
fn test_protect_profile4_empty_payload() {
let config = Profile4Config::new(0x1234_5678, 15);
let mut state = Profile4State::new();
let mut buf = [0u8; 256];
let len = protect_profile4(&config, &mut state, b"", &mut buf).unwrap();
assert_eq!(len, 12); }
#[test]
fn test_protect_profile5_empty_payload() {
let config = Profile5Config::new(0x1234, 3, 15);
let mut state = Profile5State::new();
let mut buf = [0u8; 256];
let len = protect_profile5(&config, &mut state, b"", &mut buf).unwrap();
assert_eq!(len, 3); }
}