autosar_e2e/profiles/
profile6.rs

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