autosar_e2e/profiles/
profile8.rs

1//! # E2E Profile 8 Implementation
2//!
3//! Profile 8 is designed for protecting large data packets
4//! with low overhead. It uses:
5//! - 32-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(4B) | LENGTH(4B) | COUNTER(4B) | ID (4B) | DATA ...]
12use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
13use crc::{Crc, CRC_32_AUTOSAR};
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 8
21#[derive(Debug, Clone)]
22pub struct Profile8Config {
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 8
36#[derive(Debug, Clone)]
37pub struct Profile8Check {
38    rx_data_length: u32,
39    rx_counter: u32,
40    rx_data_id: u32,
41    rx_crc: u32,
42    calculated_crc: u32,
43    data_len: u32,
44}
45
46impl Default for Profile8Config {
47    fn default() -> Self {
48        Self {
49            data_id: 0x0a0b0c0d,
50            offset: 0x00000000,
51            min_data_length: 128,        // 16bytes
52            max_data_length: 4294967295, // MAX(U32)
53            max_delta_counter: 1,
54        }
55    }
56}
57
58/// E2E Profile 8 Implementation
59///
60/// Implements AUTOSAR E2E Profile 8 protection mechanism
61#[derive(Clone)]
62pub struct Profile8 {
63    config: Profile8Config,
64    counter: u32,
65    initialized: bool,
66}
67
68impl Profile8 {
69    /// Validate configuration parameters
70    fn validate_config(config: &Profile8Config) -> E2EResult<()> {
71        if config.min_data_length < 16 * BITS_PER_BYTE {
72            return Err(E2EError::InvalidConfiguration(
73                "Minimum Data length shall be larger than 16B".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 + 4..=offset + 7].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 + 8..=offset + 11].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 + 12..=offset + 15].copy_from_slice(&self.config.data_id.to_be_bytes());
113    }
114    fn compute_crc(&self, data: &[u8]) -> u32 {
115        let crc: Crc<u32> = Crc::<u32>::new(&CRC_32_AUTOSAR);
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 + 4)..]); // crc calculation data after offset
120        digest.finalize()
121    }
122    fn write_crc(&self, calculated_crc: u32, data: &mut [u8]) {
123        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
124        data[offset..=offset + 3].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 + 4],
138            data[offset + 5],
139            data[offset + 6],
140            data[offset + 7],
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 + 8],
147            data[offset + 9],
148            data[offset + 10],
149            data[offset + 11],
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 + 12],
156            data[offset + 13],
157            data[offset + 14],
158            data[offset + 15],
159        ])
160    }
161    fn read_crc(&self, data: &[u8]) -> u32 {
162        let offset = (self.config.offset / BITS_PER_BYTE) as usize;
163        u32::from_be_bytes([
164            data[offset],
165            data[offset + 1],
166            data[offset + 2],
167            data[offset + 3],
168        ])
169    }
170
171    fn do_checks(&mut self, check_items: Profile8Check) -> E2EStatus {
172        if check_items.calculated_crc != check_items.rx_crc {
173            return E2EStatus::CrcError;
174        }
175        if check_items.rx_data_id != self.config.data_id {
176            return E2EStatus::DataIdError;
177        }
178        if check_items.rx_data_length != check_items.data_len {
179            return E2EStatus::DataLengthError;
180        }
181        let status = self.validate_counter(check_items.rx_counter);
182        self.counter = check_items.rx_counter;
183        status
184    }
185    /// Check if counter delta is within acceptable range
186    fn check_counter_delta(&self, received_counter: u32) -> u32 {
187        if received_counter >= self.counter {
188            received_counter - self.counter
189        } else {
190            // Handle wrap-around
191            ((COUNTER_MODULO + received_counter as u64 - self.counter as u64) % COUNTER_MODULO)
192                as u32
193        }
194    }
195    fn validate_counter(&self, rx_counter: u32) -> E2EStatus {
196        let delta = self.check_counter_delta(rx_counter);
197
198        if delta == 0 {
199            if self.initialized {
200                E2EStatus::Repeated
201            } else {
202                E2EStatus::Ok
203            }
204        } else if delta == 1 {
205            E2EStatus::Ok
206        } else if delta >= 2 && delta <= self.config.max_delta_counter {
207            E2EStatus::OkSomeLost
208        } else {
209            E2EStatus::WrongSequence
210        }
211    }
212}
213
214impl E2EProfile for Profile8 {
215    type Config = Profile8Config;
216
217    fn new(config: Self::Config) -> E2EResult<Self> {
218        // Validate config
219        Self::validate_config(&config)?;
220        Ok(Self {
221            config,
222            counter: 0,
223            initialized: false,
224        })
225    }
226
227    fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
228        self.validate_length(data.len() as u32)?;
229        self.write_data_length(data);
230        self.write_counter(data);
231        self.write_data_id(data);
232        let calculated_crc = self.compute_crc(data);
233        self.write_crc(calculated_crc, data);
234        self.increment_counter();
235        Ok(())
236    }
237
238    fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
239        // Check data length
240        self.validate_length(data.len() as u32)?;
241        let check_items = Profile8Check {
242            rx_data_length: self.read_data_length(data),
243            rx_counter: self.read_counter(data),
244            rx_crc: self.read_crc(data),
245            rx_data_id: self.read_data_id(data),
246            calculated_crc: self.compute_crc(data),
247            data_len: data.len() as u32,
248        };
249        let status = self.do_checks(check_items);
250        if !self.initialized && matches!(status, E2EStatus::Ok | E2EStatus::OkSomeLost) {
251            self.initialized = true;
252        }
253        Ok(status)
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260    #[test]
261    fn test_profile8_basic_example() {
262        let mut profile_tx = Profile8::new(Profile8Config::default()).unwrap();
263        let mut profile_rx = Profile8::new(Profile8Config::default()).unwrap();
264
265        let mut data = vec![
266            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268        ];
269        profile_tx.protect(&mut data).unwrap();
270        // CRC check
271        assert_eq!(data[0], 0x41);
272        assert_eq!(data[1], 0x49);
273        assert_eq!(data[2], 0x4e);
274        assert_eq!(data[3], 0x52);
275        // length check
276        assert_eq!(data[4], 0x00);
277        assert_eq!(data[5], 0x00);
278        assert_eq!(data[6], 0x00);
279        assert_eq!(data[7], 0x14);
280        // counter check
281        assert_eq!(data[8], 0x00);
282        assert_eq!(data[9], 0x00);
283        assert_eq!(data[10], 0x00);
284        assert_eq!(data[11], 0x00);
285        // data id check
286        assert_eq!(data[12], 0x0a);
287        assert_eq!(data[13], 0x0b);
288        assert_eq!(data[14], 0x0c);
289        assert_eq!(data[15], 0x0d);
290        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
291    }
292
293    #[test]
294    fn test_profile8_offset_example() {
295        let config = Profile8Config {
296            offset: 64,
297            ..Default::default()
298        };
299
300        let mut profile_tx = Profile8::new(config.clone()).unwrap();
301        let mut profile_rx = Profile8::new(config).unwrap();
302
303        let mut data = vec![
304            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306        ];
307        profile_tx.protect(&mut data).unwrap();
308        // CRC check
309        assert_eq!(data[8], 0xe8);
310        assert_eq!(data[9], 0x91);
311        assert_eq!(data[10], 0xe5);
312        assert_eq!(data[11], 0xa8);
313        // length check
314        assert_eq!(data[12], 0x00);
315        assert_eq!(data[13], 0x00);
316        assert_eq!(data[14], 0x00);
317        assert_eq!(data[15], 0x1c);
318        // counter check
319        assert_eq!(data[16], 0x00);
320        assert_eq!(data[17], 0x00);
321        assert_eq!(data[18], 0x00);
322        assert_eq!(data[19], 0x00);
323        // data id check
324        assert_eq!(data[20], 0x0a);
325        assert_eq!(data[21], 0x0b);
326        assert_eq!(data[22], 0x0c);
327        assert_eq!(data[23], 0x0d);
328        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
329    }
330    #[test]
331    fn test_profile8_counter_wraparound() {
332        let config = Profile8Config {
333            offset: 64,
334            ..Default::default()
335        };
336
337        let mut profile_tx = Profile8::new(config.clone()).unwrap();
338        let mut profile_rx = Profile8::new(config).unwrap();
339
340        let mut data = vec![
341            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343        ];
344        profile_tx.protect(&mut data).unwrap();
345        // counter check
346        assert_eq!(data[16], 0x00);
347        assert_eq!(data[17], 0x00);
348        assert_eq!(data[18], 0x00);
349        assert_eq!(data[19], 0x00);
350        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
351        profile_tx.protect(&mut data).unwrap();
352        // counter check
353        assert_eq!(data[16], 0x00);
354        assert_eq!(data[17], 0x00);
355        assert_eq!(data[18], 0x00);
356        assert_eq!(data[19], 0x01);
357        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
358        profile_rx.counter = 0xFFFFFFFE;
359        profile_tx.counter = 0xFFFFFFFF;
360        profile_tx.protect(&mut data).unwrap();
361        // counter check
362        assert_eq!(data[16], 0xFF);
363        assert_eq!(data[17], 0xFF);
364        assert_eq!(data[18], 0xFF);
365        assert_eq!(data[19], 0xFF);
366        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
367        profile_tx.protect(&mut data).unwrap();
368        // counter check
369        assert_eq!(data[16], 0x00);
370        assert_eq!(data[17], 0x00);
371        assert_eq!(data[18], 0x00);
372        assert_eq!(data[19], 0x00);
373        assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
374    }
375}