autosar_e2e/profiles/
profile4.rs

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