autosar_e2e/profiles/
profile7.rs

1//! # E2E Profile 7 Implementation
2//!
3//! Profile 7 is designed for protecting large data packets
4//! with low overhead. It uses:
5//! - 64-bit CRC for data integrity
6//! - 32-bit counter for sequence checking
7//! - 32-bit Data ID for masquerade prevention
8//! - 32-bit Data Length to support dynamic size data
9//!
10//! # Data layout
11//! [DATA ... | CRC(8B) | LENGTH(4B) | COUNTER(4B) | ID (4B) | DATA ...]
12use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
13use crc::{Crc, CRC_64_XZ};
14
15// Constants
16const BITS_PER_BYTE: u32 = 8;
17const COUNTER_MAX: u32 = 0xFFFFFFFF;
18const COUNTER_MODULO: u64 = 0x100000000;
19
20/// Configuration for E2E Profile 7
21#[derive(Debug, Clone)]
22pub struct Profile7Config {
23    /// data id
24    pub data_id: u32,
25    /// Bit offset of the first bit of the E2E header from the beginning of the Data
26    pub offset: u32,
27    /// Minimal length of Data, in bits
28    pub min_data_length: u32,
29    /// Maximal length of Data, in bits
30    pub max_data_length: u32,
31    /// Maximum allowed delta between consecutive counters
32    pub max_delta_counter: u32,
33}
34
35/// Check Item for E2E Profile 7
36#[derive(Debug, Clone)]
37pub struct Profile7Check {
38    rx_data_length: u32,
39    rx_counter: u32,
40    rx_data_id: u32,
41    rx_crc: u64,
42    calculated_crc: u64,
43    data_len: u32,
44}
45
46impl Default for Profile7Config {
47    fn default() -> Self {
48        Self {
49            data_id: 0x0a0b0c0d,
50            offset: 0x00000000,
51            min_data_length: 160,   // 20bytes
52            max_data_length: 32768, // 4096bytes
53            max_delta_counter: 1,
54        }
55    }
56}
57
58/// E2E Profile 7 Implementation
59///
60/// Implements AUTOSAR E2E Profile 7 protection mechanism
61#[derive(Clone)]
62pub struct Profile7 {
63    config: Profile7Config,
64    counter: u32,
65    initialized: bool,
66}
67
68impl Profile7 {
69    /// Validate configuration parameters
70    fn validate_config(config: &Profile7Config) -> E2EResult<()> {
71        if config.min_data_length < 20 * BITS_PER_BYTE {
72            return Err(E2EError::InvalidConfiguration(
73                "Minimum Data length shall be larger than 20B".into(),
74            ));
75        }
76        if config.max_data_length < config.min_data_length {
77            return Err(E2EError::InvalidConfiguration(
78                "Maximum Data length shall be larger than MinDataLength".into(),
79            ));
80        }
81        if config.max_delta_counter == 0 || config.max_delta_counter == COUNTER_MAX {
82            return Err(E2EError::InvalidConfiguration(format!(
83                "Max delta counter must be between 1 and {}",
84                COUNTER_MAX
85            )));
86        }
87        Ok(())
88    }
89    /// Validate data length against min/max constraints
90    fn validate_length(&self, len: u32) -> E2EResult<()> {
91        let min_bytes = self.config.min_data_length / BITS_PER_BYTE;
92        let max_bytes = self.config.max_data_length / BITS_PER_BYTE;
93        if len < min_bytes || max_bytes < len {
94            return Err(E2EError::InvalidDataFormat(format!(
95                "Expected {} - {} bytes, got {} bytes",
96                min_bytes, max_bytes, len
97            )));
98        }
99        Ok(())
100    }
101    fn write_data_length(&self, data: &mut [u8]) {
102        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
103        let len32 = data.len() as u32;
104        data[offset + 8..=offset + 11].copy_from_slice(&len32.to_be_bytes());
105    }
106    fn write_counter(&self, data: &mut [u8]) {
107        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
108        data[offset + 12..=offset + 15].copy_from_slice(&self.counter.to_be_bytes());
109    }
110    fn write_data_id(&self, data: &mut [u8]) {
111        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
112        data[offset + 16..=offset + 19].copy_from_slice(&self.config.data_id.to_be_bytes());
113    }
114    fn compute_crc(&self, data: &[u8]) -> u64 {
115        let crc: Crc<u64> = Crc::<u64>::new(&CRC_64_XZ);
116        let mut digest = crc.digest();
117        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
118        digest.update(&data[0..offset]); // crc calculation data before offset
119        digest.update(&data[(offset + 8)..]); // crc calculation data after offset
120        digest.finalize()
121    }
122    fn write_crc(&self, calculated_crc: u64, data: &mut [u8]) {
123        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
124        data[offset..=offset + 7].copy_from_slice(&calculated_crc.to_be_bytes());
125    }
126    fn increment_counter(&mut self) {
127        self.counter = if self.counter == COUNTER_MAX {
128            0x00000000
129        } else {
130            (self.counter + 1) & COUNTER_MAX
131        };
132    }
133
134    fn read_data_length(&self, data: &[u8]) -> u32 {
135        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
136        u32::from_be_bytes([
137            data[offset + 8],
138            data[offset + 9],
139            data[offset + 10],
140            data[offset + 11],
141        ])
142    }
143    fn read_counter(&self, data: &[u8]) -> u32 {
144        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
145        u32::from_be_bytes([
146            data[offset + 12],
147            data[offset + 13],
148            data[offset + 14],
149            data[offset + 15],
150        ])
151    }
152    fn read_data_id(&self, data: &[u8]) -> u32 {
153        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
154        u32::from_be_bytes([
155            data[offset + 16],
156            data[offset + 17],
157            data[offset + 18],
158            data[offset + 19],
159        ])
160    }
161    fn read_crc(&self, data: &[u8]) -> u64 {
162        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
163        u64::from_be_bytes([
164            data[offset],
165            data[offset + 1],
166            data[offset + 2],
167            data[offset + 3],
168            data[offset + 4],
169            data[offset + 5],
170            data[offset + 6],
171            data[offset + 7],
172        ])
173    }
174
175    fn do_checks(&mut self, check_items: Profile7Check) -> E2EStatus {
176        if check_items.calculated_crc != check_items.rx_crc {
177            return E2EStatus::CrcError;
178        }
179        if check_items.rx_data_id != self.config.data_id {
180            return E2EStatus::DataIdError;
181        }
182        if check_items.rx_data_length != check_items.data_len {
183            return E2EStatus::DataLengthError;
184        }
185        let status = self.validate_counter(check_items.rx_counter);
186        self.counter = check_items.rx_counter;
187        status
188    }
189    /// Check if counter delta is within acceptable range
190    fn check_counter_delta(&self, received_counter: u32) -> u32 {
191        if received_counter >= self.counter {
192            received_counter - self.counter
193        } else {
194            // Handle wrap-around
195            ((COUNTER_MODULO + received_counter as u64 - self.counter as u64) % COUNTER_MODULO)
196                as u32
197        }
198    }
199    fn validate_counter(&self, rx_counter: u32) -> E2EStatus {
200        let delta = self.check_counter_delta(rx_counter);
201
202        if delta == 0 {
203            if self.initialized {
204                E2EStatus::Repeated
205            } else {
206                E2EStatus::Ok
207            }
208        } else if delta == 1 {
209            E2EStatus::Ok
210        } else if delta >= 2 && delta <= self.config.max_delta_counter {
211            E2EStatus::OkSomeLost
212        } else {
213            E2EStatus::WrongSequence
214        }
215    }
216}
217
218impl E2EProfile for Profile7 {
219    type Config = Profile7Config;
220
221    fn new(config: Self::Config) -> E2EResult<Self> {
222        // Validate config
223        Self::validate_config(&config)?;
224        Ok(Self {
225            config,
226            counter: 0,
227            initialized: false,
228        })
229    }
230
231    fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
232        self.validate_length(data.len() as u32)?;
233        self.write_data_length(data);
234        self.write_counter(data);
235        self.write_data_id(data);
236        let calculated_crc = self.compute_crc(data);
237        self.write_crc(calculated_crc, data);
238        self.increment_counter();
239        Ok(())
240    }
241
242    fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
243        // Check data length
244        self.validate_length(data.len() as u32)?;
245        let check_items = Profile7Check {
246            rx_data_length: self.read_data_length(data),
247            rx_counter: self.read_counter(data),
248            rx_crc: self.read_crc(data),
249            rx_data_id: self.read_data_id(data),
250            calculated_crc: self.compute_crc(data),
251            data_len: data.len() as u32,
252        };
253        let status = self.do_checks(check_items);
254        if !self.initialized && matches!(status, E2EStatus::Ok | E2EStatus::OkSomeLost) {
255            self.initialized = true;
256        }
257        Ok(status)
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264    #[test]
265    fn test_profile7_basic_example() {
266        let mut profile_tx = Profile7::new(Profile7Config::default()).unwrap();
267        let mut profile_rx = Profile7::new(Profile7Config::default()).unwrap();
268
269        let mut data = vec![
270            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272        ];
273        profile_tx.protect(&mut data).unwrap();
274        // CRC check
275        assert_eq!(data[0], 0x1f);
276        assert_eq!(data[1], 0xb2);
277        assert_eq!(data[2], 0xe7);
278        assert_eq!(data[3], 0x37);
279        assert_eq!(data[4], 0xfc);
280        assert_eq!(data[5], 0xed);
281        assert_eq!(data[6], 0xbc);
282        assert_eq!(data[7], 0xd9);
283        // length check
284        assert_eq!(data[8], 0x00);
285        assert_eq!(data[9], 0x00);
286        assert_eq!(data[10], 0x00);
287        assert_eq!(data[11], 0x18);
288        // counter check
289        assert_eq!(data[12], 0x00);
290        assert_eq!(data[13], 0x00);
291        assert_eq!(data[14], 0x00);
292        assert_eq!(data[15], 0x00);
293        // data id check
294        assert_eq!(data[16], 0x0a);
295        assert_eq!(data[17], 0x0b);
296        assert_eq!(data[18], 0x0c);
297        assert_eq!(data[19], 0x0d);
298        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
299    }
300
301    #[test]
302    fn test_profile7_offset_example() {
303        let config = Profile7Config {
304            offset: 64,
305            ..Default::default()
306        };
307
308        let mut profile_tx = Profile7::new(config.clone()).unwrap();
309        let mut profile_rx = Profile7::new(config).unwrap();
310
311        let mut data = vec![
312            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314            0x00, 0x00, 0x00, 0x00,
315        ];
316        profile_tx.protect(&mut data).unwrap();
317        // CRC check
318        assert_eq!(data[8], 0x17);
319        assert_eq!(data[9], 0xf7);
320        assert_eq!(data[10], 0xc8);
321        assert_eq!(data[11], 0x17);
322        assert_eq!(data[12], 0x32);
323        assert_eq!(data[13], 0x38);
324        assert_eq!(data[14], 0x65);
325        assert_eq!(data[15], 0xa8);
326        // length check
327        assert_eq!(data[16], 0x00);
328        assert_eq!(data[17], 0x00);
329        assert_eq!(data[18], 0x00);
330        assert_eq!(data[19], 0x20);
331        // counter check
332        assert_eq!(data[20], 0x00);
333        assert_eq!(data[21], 0x00);
334        assert_eq!(data[22], 0x00);
335        assert_eq!(data[23], 0x00);
336        // data id check
337        assert_eq!(data[24], 0x0a);
338        assert_eq!(data[25], 0x0b);
339        assert_eq!(data[26], 0x0c);
340        assert_eq!(data[27], 0x0d);
341        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
342    }
343    #[test]
344    fn test_profile7_counter_wraparound() {
345        let config = Profile7Config {
346            offset: 64,
347            ..Default::default()
348        };
349
350        let mut profile_tx = Profile7::new(config.clone()).unwrap();
351        let mut profile_rx = Profile7::new(config).unwrap();
352
353        let mut data = vec![
354            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356            0x00, 0x00, 0x00, 0x00,
357        ];
358        profile_tx.protect(&mut data).unwrap();
359        // counter check
360        assert_eq!(data[20], 0x00);
361        assert_eq!(data[21], 0x00);
362        assert_eq!(data[22], 0x00);
363        assert_eq!(data[23], 0x00);
364        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
365        profile_tx.protect(&mut data).unwrap();
366        // counter check
367        assert_eq!(data[20], 0x00);
368        assert_eq!(data[21], 0x00);
369        assert_eq!(data[22], 0x00);
370        assert_eq!(data[23], 0x01);
371        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
372        profile_rx.counter = 0xFFFFFFFE;
373        profile_tx.counter = 0xFFFFFFFF;
374        profile_tx.protect(&mut data).unwrap();
375        // counter check
376        assert_eq!(data[20], 0xFF);
377        assert_eq!(data[21], 0xFF);
378        assert_eq!(data[22], 0xFF);
379        assert_eq!(data[23], 0xFF);
380        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
381        profile_tx.protect(&mut data).unwrap();
382        // counter check
383        assert_eq!(data[20], 0x00);
384        assert_eq!(data[21], 0x00);
385        assert_eq!(data[22], 0x00);
386        assert_eq!(data[23], 0x00);
387        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
388    }
389}