1mod config;
28mod crc;
29mod e2e_checker;
30mod e2e_protector;
31mod error;
32#[cfg(feature = "std")]
33mod registry;
34mod state;
35
36pub use config::{Profile4Config, Profile5Config};
37pub use e2e_checker::{check_profile4, check_profile5, check_profile5_with_header};
38pub use e2e_protector::{
39 PROFILE4_HEADER_SIZE, PROFILE5_HEADER_SIZE, protect_profile4, protect_profile5,
40 protect_profile5_with_header,
41};
42pub use error::Error;
43#[cfg(feature = "std")]
44pub use registry::E2ERegistry;
45pub use state::{Profile4State, Profile5State};
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum E2ECheckStatus {
50 Unchecked,
52 Ok,
54 CrcError,
56 Repeated,
58 OkSomeLost,
60 WrongSequence,
62 BadArgument,
64}
65
66impl E2ECheckStatus {
67 #[must_use]
69 pub fn to_return_code(self) -> u8 {
70 match self {
71 E2ECheckStatus::Unchecked => 0,
72 E2ECheckStatus::Ok => 1,
73 E2ECheckStatus::CrcError => 2,
74 E2ECheckStatus::Repeated => 3,
75 E2ECheckStatus::OkSomeLost => 4,
76 E2ECheckStatus::WrongSequence => 5,
77 E2ECheckStatus::BadArgument => 6,
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
84pub struct E2ECheckResult<'a> {
85 pub status: E2ECheckStatus,
87 pub counter: Option<u32>,
89 pub payload: Option<&'a [u8]>,
94}
95
96impl<'a> E2ECheckResult<'a> {
97 pub(crate) fn error(status: E2ECheckStatus) -> Self {
98 Self {
99 status,
100 counter: None,
101 payload: None,
102 }
103 }
104
105 pub(crate) fn success(status: E2ECheckStatus, counter: u32, payload: &'a [u8]) -> Self {
106 Self {
107 status,
108 counter: Some(counter),
109 payload: Some(payload),
110 }
111 }
112
113 #[cfg(feature = "std")]
117 #[must_use]
118 pub fn to_owned_payload(&self) -> Option<std::vec::Vec<u8>> {
119 self.payload.map(<[u8]>::to_vec)
120 }
121}
122
123#[derive(Debug, Clone)]
125pub enum E2EProfile {
126 Profile4(Profile4Config),
128 Profile5(Profile5Config),
130 Profile5WithHeader(Profile5Config),
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136pub struct E2EKey {
137 pub service_id: u16,
139 pub method_or_event_id: u16,
141}
142
143impl E2EKey {
144 #[must_use]
146 pub const fn new(service_id: u16, method_or_event_id: u16) -> Self {
147 Self {
148 service_id,
149 method_or_event_id,
150 }
151 }
152
153 #[must_use]
155 pub fn from_message_id(message_id: crate::protocol::MessageId) -> Self {
156 Self {
157 service_id: message_id.service_id(),
158 method_or_event_id: message_id.method_id(),
159 }
160 }
161}
162
163#[cfg(feature = "std")]
165#[derive(Debug, Clone)]
166pub(crate) enum E2EState {
167 Profile4(Profile4State),
169 Profile5(Profile5State),
171}
172
173#[cfg(feature = "std")]
174impl E2EState {
175 pub(crate) fn from_profile(profile: &E2EProfile) -> Self {
176 match profile {
177 E2EProfile::Profile4(_) => Self::Profile4(Profile4State::new()),
178 E2EProfile::Profile5(_) | E2EProfile::Profile5WithHeader(_) => {
179 Self::Profile5(Profile5State::new())
180 }
181 }
182 }
183}
184
185#[cfg(feature = "std")]
188pub(crate) fn e2e_check<'a>(
189 profile: &E2EProfile,
190 state: &mut E2EState,
191 payload: &'a [u8],
192 upper_header: [u8; 8],
193) -> (E2ECheckStatus, &'a [u8]) {
194 let result = match (profile, state) {
195 (E2EProfile::Profile4(config), E2EState::Profile4(st)) => {
196 check_profile4(config, st, payload)
197 }
198 (E2EProfile::Profile5(config), E2EState::Profile5(st)) => {
199 check_profile5(config, st, payload)
200 }
201 (E2EProfile::Profile5WithHeader(config), E2EState::Profile5(st)) => {
202 check_profile5_with_header(config, st, payload, upper_header)
203 }
204 _ => return (E2ECheckStatus::BadArgument, payload),
205 };
206 let stripped = result.payload.unwrap_or(payload);
207 (result.status, stripped)
208}
209
210#[cfg(feature = "std")]
216pub(crate) fn e2e_protect(
217 profile: &E2EProfile,
218 state: &mut E2EState,
219 payload: &[u8],
220 upper_header: [u8; 8],
221 output: &mut [u8],
222) -> Result<usize, Error> {
223 match (profile, state) {
224 (E2EProfile::Profile4(config), E2EState::Profile4(st)) => {
225 protect_profile4(config, st, payload, output)
226 }
227 (E2EProfile::Profile5(config), E2EState::Profile5(st)) => {
228 protect_profile5(config, st, payload, output)
229 }
230 (E2EProfile::Profile5WithHeader(config), E2EState::Profile5(st)) => {
231 protect_profile5_with_header(config, st, payload, upper_header, output)
232 }
233 _ => unreachable!("E2EState is always created from E2EProfile"),
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_status_return_codes() {
243 assert_eq!(E2ECheckStatus::Unchecked.to_return_code(), 0);
244 assert_eq!(E2ECheckStatus::Ok.to_return_code(), 1);
245 assert_eq!(E2ECheckStatus::CrcError.to_return_code(), 2);
246 assert_eq!(E2ECheckStatus::Repeated.to_return_code(), 3);
247 assert_eq!(E2ECheckStatus::OkSomeLost.to_return_code(), 4);
248 assert_eq!(E2ECheckStatus::WrongSequence.to_return_code(), 5);
249 assert_eq!(E2ECheckStatus::BadArgument.to_return_code(), 6);
250 }
251
252 #[test]
253 fn test_profile4_roundtrip() {
254 let config = Profile4Config::new(0x12345678, 15);
255 let mut protect_state = Profile4State::new();
256 let mut check_state = Profile4State::new();
257
258 let payload = b"Test payload data";
259 let mut buf = [0u8; 256];
260 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
261 let protected = &buf[..len];
262
263 assert_eq!(len, payload.len() + 12); let result = check_profile4(&config, &mut check_state, protected);
266 assert_eq!(result.status, E2ECheckStatus::Ok);
267 assert_eq!(result.counter, Some(0));
268 assert_eq!(result.payload, Some(payload.as_slice()));
269 }
270
271 #[test]
272 fn test_profile5_roundtrip() {
273 let config = Profile5Config::new(0x1234, 20, 15);
274 let mut protect_state = Profile5State::new();
275 let mut check_state = Profile5State::new();
276
277 let mut payload = [0u8; 20];
279 payload[..17].copy_from_slice(b"Test payload data");
280 let mut buf = [0u8; 256];
281 let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
282 let protected = &buf[..len];
283
284 assert_eq!(len, payload.len() + 3); let result = check_profile5(&config, &mut check_state, protected);
287 assert_eq!(result.status, E2ECheckStatus::Ok);
288 assert_eq!(result.counter, Some(0));
289 assert_eq!(result.payload, Some(payload.as_slice()));
290 }
291
292 #[test]
293 fn test_profile4_sequence_detection() {
294 let config = Profile4Config::new(0x12345678, 5);
295 let mut protect_state = Profile4State::new();
296 let mut check_state = Profile4State::new();
297
298 let payload = b"Test";
299 let mut buf1 = [0u8; 256];
300 let mut buf2 = [0u8; 256];
301
302 let len1 = protect_profile4(&config, &mut protect_state, payload, &mut buf1).unwrap();
304 let result1 = check_profile4(&config, &mut check_state, &buf1[..len1]);
305 assert_eq!(result1.status, E2ECheckStatus::Ok);
306
307 let len2 = protect_profile4(&config, &mut protect_state, payload, &mut buf2).unwrap();
309 let result2 = check_profile4(&config, &mut check_state, &buf2[..len2]);
310 assert_eq!(result2.status, E2ECheckStatus::Ok);
311
312 let result3 = check_profile4(&config, &mut check_state, &buf1[..len1]);
314 assert!(matches!(
315 result3.status,
316 E2ECheckStatus::Repeated | E2ECheckStatus::WrongSequence
317 ));
318 }
319
320 #[test]
321 fn test_profile4_some_lost_detection() {
322 let config = Profile4Config::new(0x12345678, 5);
323 let mut protect_state = Profile4State::new();
324 let mut check_state = Profile4State::new();
325
326 let payload = b"Test";
327 let mut buf = [0u8; 256];
328
329 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
331 let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
332 assert_eq!(result1.status, E2ECheckStatus::Ok);
333
334 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
336 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
337 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
338
339 let result4 = check_profile4(&config, &mut check_state, &buf[..len]);
341 assert_eq!(result4.status, E2ECheckStatus::OkSomeLost);
342 }
343
344 #[test]
345 fn test_profile4_wrong_sequence_detection() {
346 let config = Profile4Config::new(0x12345678, 2);
347 let mut protect_state = Profile4State::new();
348 let mut check_state = Profile4State::new();
349
350 let payload = b"Test";
351 let mut buf = [0u8; 256];
352
353 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
355 let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
356 assert_eq!(result1.status, E2ECheckStatus::Ok);
357
358 for _ in 0..5 {
360 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
361 }
362 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
363
364 let result = check_profile4(&config, &mut check_state, &buf[..len]);
366 assert_eq!(result.status, E2ECheckStatus::WrongSequence);
367 }
368
369 #[test]
370 fn test_profile4_crc_error() {
371 let config = Profile4Config::new(0x12345678, 15);
372 let mut protect_state = Profile4State::new();
373 let mut check_state = Profile4State::new();
374
375 let payload = b"Test";
376 let mut buf = [0u8; 256];
377 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
378
379 buf[8] ^= 0xFF;
381
382 let result = check_profile4(&config, &mut check_state, &buf[..len]);
383 assert_eq!(result.status, E2ECheckStatus::CrcError);
384 }
385
386 #[test]
387 fn test_profile5_crc_error() {
388 let config = Profile5Config::new(0x1234, 20, 15);
389 let mut protect_state = Profile5State::new();
390 let mut check_state = Profile5State::new();
391
392 let mut payload = [0u8; 20];
393 payload[..4].copy_from_slice(b"Test");
394 let mut buf = [0u8; 256];
395 let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
396
397 buf[1] ^= 0xFF;
399
400 let result = check_profile5(&config, &mut check_state, &buf[..len]);
401 assert_eq!(result.status, E2ECheckStatus::CrcError);
402 }
403
404 #[test]
405 fn test_profile4_bad_argument_short_message() {
406 let config = Profile4Config::new(0x12345678, 15);
407 let mut check_state = Profile4State::new();
408
409 let short_message = [0u8; 8];
411 let result = check_profile4(&config, &mut check_state, &short_message);
412 assert_eq!(result.status, E2ECheckStatus::BadArgument);
413 }
414
415 #[test]
416 fn test_profile5_bad_argument_short_message() {
417 let config = Profile5Config::new(0x1234, 20, 15);
418 let mut check_state = Profile5State::new();
419
420 let short_message = [0u8; 2];
422 let result = check_profile5(&config, &mut check_state, &short_message);
423 assert_eq!(result.status, E2ECheckStatus::BadArgument);
424 }
425
426 #[cfg(feature = "std")]
427 #[test]
428 fn test_check_result_to_owned_payload() {
429 let data = b"hello";
430 let result = E2ECheckResult::success(E2ECheckStatus::Ok, 0, data);
431 let owned = result.to_owned_payload();
432 assert_eq!(owned, Some(b"hello".to_vec()));
433
434 let err_result = E2ECheckResult::error(E2ECheckStatus::CrcError);
435 assert_eq!(err_result.to_owned_payload(), None);
436 }
437
438 #[test]
439 fn test_e2e_key_from_message_id() {
440 let mid = crate::protocol::MessageId::new_from_service_and_method(0x1234, 0x0001);
441 let key = E2EKey::from_message_id(mid);
442 assert_eq!(key.service_id, 0x1234);
443 assert_eq!(key.method_or_event_id, 0x0001);
444 }
445}