autosar_e2e/profiles/
profile22.rs

1//! # E2E Profile 22 Implementation
2//!
3//! Profile 22 is designed for protecting small data packets
4//! with low overhead. It uses:
5//! - 8-bit CRC for data integrity
6//! - 4-bit counter for sequence checking (0-15)
7//! - 16-bit Data ID for masquerade prevention
8//!
9//! # Data layout
10//! [DATA ... | CRC(1B) | HDR(1B) | DATA ...]
11//! - HDR (bits 3..0) : counter
12
13use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
14use crc::{Crc, CRC_8_AUTOSAR};
15
16// Constants
17const COUNTER_MASK: u8 = 0x0F;
18const COUNTER_MAX: u8 = 15;
19const COUNTER_MODULO: u8 = 16;
20const BITS_PER_BYTE: usize = 8;
21const DATA_ID_NUMBER: usize = 16;
22
23/// Configuration for E2E Profile 22
24#[derive(Debug, Clone)]
25pub struct Profile22Config {
26    /// Length of Data, in bits. The value shall be a multiple of 8.
27    pub data_length: usize,
28    /// An array of appropriately chosen Data IDs for protection against masquerading.
29    pub data_id_list: [u8; DATA_ID_NUMBER],
30    /// Maximum allowed delta between consecutive counters
31    pub max_delta_counter: u8,
32    /// Bit offset of E2E header in the Data[] array in bits.
33    pub offset: usize,
34}
35
36impl Default for Profile22Config {
37    fn default() -> Self {
38        Self {
39            data_length: 64, // bits
40            data_id_list: [
41                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
42                0x0f, 0x10,
43            ],
44            max_delta_counter: 1,
45            offset: 0, // bits
46        }
47    }
48}
49
50/// Check Item for E2E Profile 22
51#[derive(Debug, Clone)]
52pub struct Profile22Check {
53    rx_counter: u8,
54    rx_crc: u8,
55    calculated_crc: u8,
56}
57/// E2E Profile 22 Implementation
58///
59/// Implements AUTOSAR E2E Profile 22 protection mechanism
60#[derive(Clone)]
61pub struct Profile22 {
62    config: Profile22Config,
63    counter: u8,
64}
65
66impl Profile22 {
67    /// Validate configuration parameters
68    fn validate_config(config: &Profile22Config) -> E2EResult<()> {
69        if !config.data_length.is_multiple_of(BITS_PER_BYTE) {
70            return Err(E2EError::InvalidConfiguration(
71                "Data length shall be a multiple of 8".into(),
72            ));
73        }
74
75        if config.max_delta_counter == 0 || config.max_delta_counter > COUNTER_MAX {
76            return Err(E2EError::InvalidConfiguration(format!(
77                "Max delta counter must be between 1 and {}",
78                COUNTER_MAX
79            )));
80        }
81
82        Ok(())
83    }
84    /// Validate data length
85    fn validate_length(&self, len: usize) -> E2EResult<()> {
86        let expected_bytes = self.config.data_length / BITS_PER_BYTE;
87        if len != expected_bytes {
88            return Err(E2EError::InvalidDataFormat(format!(
89                "Expected {} bytes, got {} bytes",
90                expected_bytes, len
91            )));
92        }
93        Ok(())
94    }
95    fn increment_counter(&mut self) {
96        self.counter = (self.counter + 1) % COUNTER_MODULO;
97    }
98    fn write_counter(&self, data: &mut [u8]) {
99        let byte_idx = self.config.offset >> 3;
100
101        data[byte_idx + 1] = (data[byte_idx + 1] & 0xF0) | self.counter;
102    }
103    fn read_counter(&self, data: &[u8]) -> u8 {
104        let byte_idx = self.config.offset >> 3;
105
106        data[byte_idx + 1] & COUNTER_MASK
107    }
108    fn write_crc(&self, calculated_crc: u8, data: &mut [u8]) {
109        let byte_position = self.config.offset / BITS_PER_BYTE;
110        data[byte_position] = calculated_crc;
111    }
112    fn read_crc(&self, data: &[u8]) -> u8 {
113        let byte_position = self.config.offset / BITS_PER_BYTE;
114        data[byte_position]
115    }
116    fn compute_crc(&self, data: &[u8]) -> u8 {
117        let crc: Crc<u8> = Crc::<u8>::new(&CRC_8_AUTOSAR);
118        let mut digest = crc.digest();
119        let offset_byte = self.config.offset / BITS_PER_BYTE;
120        digest.update(&data[0..offset_byte]); // crc calculation data before offset
121        digest.update(&data[(offset_byte + 1)..]); // crc calculation data after offset
122        digest.update(&[self.config.data_id_list[self.read_counter(data) as usize]]); // crc calculation data id
123        digest.finalize()
124    }
125    fn do_checks(&mut self, check_items: Profile22Check) -> E2EStatus {
126        if check_items.calculated_crc != check_items.rx_crc {
127            return E2EStatus::CrcError;
128        }
129        let status = self.validate_counter(check_items.rx_counter);
130        self.counter = check_items.rx_counter;
131        status
132    }
133    /// Check if counter delta is within acceptable range
134    fn check_counter_delta(&self, received_counter: u8) -> u8 {
135        if received_counter >= self.counter {
136            received_counter - self.counter
137        } else {
138            // Handle wrap-around
139            (COUNTER_MODULO + received_counter - self.counter) % COUNTER_MODULO
140        }
141    }
142    fn validate_counter(&self, rx_counter: u8) -> E2EStatus {
143        let delta = self.check_counter_delta(rx_counter);
144
145        if delta == 0 {
146            E2EStatus::Repeated
147        } else if delta == 1 {
148            E2EStatus::Ok
149        } else if delta >= 2 && delta <= self.config.max_delta_counter {
150            E2EStatus::OkSomeLost
151        } else {
152            E2EStatus::WrongSequence
153        }
154    }
155}
156
157impl E2EProfile for Profile22 {
158    type Config = Profile22Config;
159
160    fn new(config: Self::Config) -> E2EResult<Self> {
161        // Validate config
162        Self::validate_config(&config)?;
163        Ok(Self { config, counter: 0 })
164    }
165
166    fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
167        self.validate_length(data.len())?;
168        self.increment_counter();
169        self.write_counter(data);
170        let calculated_crc = self.compute_crc(data);
171        self.write_crc(calculated_crc, data);
172        Ok(())
173    }
174
175    fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
176        // Check data length
177        self.validate_length(data.len())?;
178        let check_items = Profile22Check {
179            rx_counter: self.read_counter(data),
180            rx_crc: self.read_crc(data),
181            calculated_crc: self.compute_crc(data),
182        };
183        let status = self.do_checks(check_items);
184        Ok(status)
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    #[test]
192    fn test_profile22_basic_example() {
193        let mut profile_tx = Profile22::new(Profile22Config::default()).unwrap();
194        let mut profile_rx = Profile22::new(Profile22Config::default()).unwrap();
195
196        let mut data = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
197        profile_tx.protect(&mut data).unwrap();
198        assert_eq!(data[0], 0x1b);
199        assert_eq!(data[1], 0x01);
200        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
201        profile_tx.protect(&mut data).unwrap();
202        assert_eq!(data[0], 0x98);
203        assert_eq!(data[1], 0x02);
204        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
205        profile_tx.protect(&mut data).unwrap();
206        assert_eq!(data[0], 0x31);
207        assert_eq!(data[1], 0x03);
208        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
209        profile_tx.protect(&mut data).unwrap();
210        assert_eq!(data[0], 0x0d);
211        assert_eq!(data[1], 0x04);
212        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
213        profile_tx.protect(&mut data).unwrap();
214        assert_eq!(data[0], 0x18);
215        assert_eq!(data[1], 0x05);
216        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
217        profile_tx.protect(&mut data).unwrap();
218        assert_eq!(data[0], 0x9b);
219        assert_eq!(data[1], 0x06);
220        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
221        profile_tx.protect(&mut data).unwrap();
222        assert_eq!(data[0], 0x65);
223        assert_eq!(data[1], 0x07);
224        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
225        profile_tx.protect(&mut data).unwrap();
226        assert_eq!(data[0], 0x08);
227        assert_eq!(data[1], 0x08);
228        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
229        profile_tx.protect(&mut data).unwrap();
230        assert_eq!(data[0], 0x1d);
231        assert_eq!(data[1], 0x09);
232        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
233        profile_tx.protect(&mut data).unwrap();
234        assert_eq!(data[0], 0x9e);
235        assert_eq!(data[1], 0x0a);
236        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
237        profile_tx.protect(&mut data).unwrap();
238        assert_eq!(data[0], 0x37);
239        assert_eq!(data[1], 0x0b);
240        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
241        profile_tx.protect(&mut data).unwrap();
242        assert_eq!(data[0], 0x0b);
243        assert_eq!(data[1], 0x0c);
244        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
245        profile_tx.protect(&mut data).unwrap();
246        assert_eq!(data[0], 0x1e);
247        assert_eq!(data[1], 0x0d);
248        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
249        profile_tx.protect(&mut data).unwrap();
250        assert_eq!(data[0], 0x9d);
251        assert_eq!(data[1], 0x0e);
252        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
253        profile_tx.protect(&mut data).unwrap();
254        assert_eq!(data[0], 0xcd);
255        assert_eq!(data[1], 0x0f);
256        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
257        profile_tx.protect(&mut data).unwrap();
258        assert_eq!(data[0], 0x0e);
259        assert_eq!(data[1], 0x00);
260        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
261    }
262
263    #[test]
264    fn test_profile22_offset_example() {
265        let config = Profile22Config {
266            offset: 64,
267            data_length: 128,
268            ..Default::default()
269        };
270
271        let mut profile_tx = Profile22::new(config.clone()).unwrap();
272        let mut profile_rx = Profile22::new(config).unwrap();
273
274        let mut data1 = vec![
275            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276            0x00, 0x00,
277        ];
278        profile_tx.protect(&mut data1).unwrap();
279        assert_eq!(data1[8], 0x14);
280        assert_eq!(data1[9], 0x01);
281        assert_eq!(profile_rx.check(&data1).unwrap(), E2EStatus::Ok);
282    }
283}