1mod config;
57mod crc;
58mod e2e_checker;
59mod e2e_protector;
60mod error;
61mod registry;
62mod state;
63
64pub use config::{Profile4Config, Profile5Config};
65pub use e2e_checker::{check_profile4, check_profile5, check_profile5_with_header};
66pub use e2e_protector::{
67 PROFILE4_HEADER_SIZE, PROFILE5_HEADER_SIZE, protect_profile4, protect_profile5,
68 protect_profile5_with_header,
69};
70pub use error::Error;
71pub use registry::{E2E_REGISTRY_CAP, E2E_RX_STATE_CAP, E2ERegistry, E2ERegistryFull};
72pub use state::{Profile4State, Profile5State};
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum E2ECheckStatus {
77 Unchecked,
79 Ok,
81 CrcError,
83 Repeated,
85 OkSomeLost,
87 WrongSequence,
89 BadArgument,
91}
92
93impl E2ECheckStatus {
94 #[must_use]
96 pub fn to_return_code(self) -> u8 {
97 match self {
98 E2ECheckStatus::Unchecked => 0,
99 E2ECheckStatus::Ok => 1,
100 E2ECheckStatus::CrcError => 2,
101 E2ECheckStatus::Repeated => 3,
102 E2ECheckStatus::OkSomeLost => 4,
103 E2ECheckStatus::WrongSequence => 5,
104 E2ECheckStatus::BadArgument => 6,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct E2ECheckResult<'a> {
112 pub status: E2ECheckStatus,
114 pub counter: Option<u32>,
116 pub payload: Option<&'a [u8]>,
121}
122
123impl<'a> E2ECheckResult<'a> {
124 pub(crate) fn error(status: E2ECheckStatus) -> Self {
125 Self {
126 status,
127 counter: None,
128 payload: None,
129 }
130 }
131
132 pub(crate) fn success(status: E2ECheckStatus, counter: u32, payload: &'a [u8]) -> Self {
133 Self {
134 status,
135 counter: Some(counter),
136 payload: Some(payload),
137 }
138 }
139
140 #[cfg(feature = "std")]
144 #[must_use]
145 pub fn to_owned_payload(&self) -> Option<std::vec::Vec<u8>> {
146 self.payload.map(<[u8]>::to_vec)
147 }
148}
149
150#[derive(Debug, Clone)]
152pub enum E2EProfile {
153 Profile4(Profile4Config),
155 Profile5(Profile5Config),
157 Profile5WithHeader(Profile5Config),
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub struct E2EKey {
164 pub service_id: u16,
166 pub method_or_event_id: u16,
168}
169
170impl E2EKey {
171 #[must_use]
173 pub const fn new(service_id: u16, method_or_event_id: u16) -> Self {
174 Self {
175 service_id,
176 method_or_event_id,
177 }
178 }
179
180 #[must_use]
182 pub fn from_message_id(message_id: crate::protocol::MessageId) -> Self {
183 Self {
184 service_id: message_id.service_id(),
185 method_or_event_id: message_id.method_id(),
186 }
187 }
188}
189
190#[derive(Debug, Clone)]
192pub(crate) enum E2EState {
193 Profile4(Profile4State),
195 Profile5(Profile5State),
197}
198
199impl E2EState {
200 pub(crate) fn from_profile(profile: &E2EProfile) -> Self {
201 match profile {
202 E2EProfile::Profile4(_) => Self::Profile4(Profile4State::new()),
203 E2EProfile::Profile5(_) | E2EProfile::Profile5WithHeader(_) => {
204 Self::Profile5(Profile5State::new())
205 }
206 }
207 }
208}
209
210pub(crate) fn e2e_check<'a>(
213 profile: &E2EProfile,
214 state: &mut E2EState,
215 payload: &'a [u8],
216 upper_header: [u8; 8],
217) -> (E2ECheckStatus, &'a [u8]) {
218 let result = match (profile, state) {
219 (E2EProfile::Profile4(config), E2EState::Profile4(st)) => {
220 check_profile4(config, st, payload)
221 }
222 (E2EProfile::Profile5(config), E2EState::Profile5(st)) => {
223 check_profile5(config, st, payload)
224 }
225 (E2EProfile::Profile5WithHeader(config), E2EState::Profile5(st)) => {
226 check_profile5_with_header(config, st, payload, upper_header)
227 }
228 _ => return (E2ECheckStatus::BadArgument, payload),
229 };
230 let stripped = result.payload.unwrap_or(payload);
231 (result.status, stripped)
232}
233
234pub(crate) fn e2e_protect(
240 profile: &E2EProfile,
241 state: &mut E2EState,
242 payload: &[u8],
243 upper_header: [u8; 8],
244 output: &mut [u8],
245) -> Result<usize, Error> {
246 match (profile, state) {
247 (E2EProfile::Profile4(config), E2EState::Profile4(st)) => {
248 protect_profile4(config, st, payload, output)
249 }
250 (E2EProfile::Profile5(config), E2EState::Profile5(st)) => {
251 protect_profile5(config, st, payload, output)
252 }
253 (E2EProfile::Profile5WithHeader(config), E2EState::Profile5(st)) => {
254 protect_profile5_with_header(config, st, payload, upper_header, output)
255 }
256 _ => unreachable!("E2EState is always created from E2EProfile"),
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn test_status_return_codes() {
266 assert_eq!(E2ECheckStatus::Unchecked.to_return_code(), 0);
267 assert_eq!(E2ECheckStatus::Ok.to_return_code(), 1);
268 assert_eq!(E2ECheckStatus::CrcError.to_return_code(), 2);
269 assert_eq!(E2ECheckStatus::Repeated.to_return_code(), 3);
270 assert_eq!(E2ECheckStatus::OkSomeLost.to_return_code(), 4);
271 assert_eq!(E2ECheckStatus::WrongSequence.to_return_code(), 5);
272 assert_eq!(E2ECheckStatus::BadArgument.to_return_code(), 6);
273 }
274
275 #[test]
276 fn test_profile4_roundtrip() {
277 let config = Profile4Config::new(0x1234_5678, 15);
278 let mut protect_state = Profile4State::new();
279 let mut check_state = Profile4State::new();
280
281 let payload = b"Test payload data";
282 let mut buf = [0u8; 256];
283 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
284 let protected = &buf[..len];
285
286 assert_eq!(len, payload.len() + 12); let result = check_profile4(&config, &mut check_state, protected);
289 assert_eq!(result.status, E2ECheckStatus::Ok);
290 assert_eq!(result.counter, Some(0));
291 assert_eq!(result.payload, Some(payload.as_slice()));
292 }
293
294 #[test]
295 fn test_profile5_roundtrip() {
296 let config = Profile5Config::new(0x1234, 20, 15);
297 let mut protect_state = Profile5State::new();
298 let mut check_state = Profile5State::new();
299
300 let mut payload = [0u8; 20];
302 payload[..17].copy_from_slice(b"Test payload data");
303 let mut buf = [0u8; 256];
304 let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
305 let protected = &buf[..len];
306
307 assert_eq!(len, payload.len() + 3); let result = check_profile5(&config, &mut check_state, protected);
310 assert_eq!(result.status, E2ECheckStatus::Ok);
311 assert_eq!(result.counter, Some(0));
312 assert_eq!(result.payload, Some(payload.as_slice()));
313 }
314
315 #[test]
316 fn test_profile4_sequence_detection() {
317 let config = Profile4Config::new(0x1234_5678, 5);
318 let mut protect_state = Profile4State::new();
319 let mut check_state = Profile4State::new();
320
321 let payload = b"Test";
322 let mut buf1 = [0u8; 256];
323 let mut buf2 = [0u8; 256];
324
325 let len1 = protect_profile4(&config, &mut protect_state, payload, &mut buf1).unwrap();
327 let result1 = check_profile4(&config, &mut check_state, &buf1[..len1]);
328 assert_eq!(result1.status, E2ECheckStatus::Ok);
329
330 let len2 = protect_profile4(&config, &mut protect_state, payload, &mut buf2).unwrap();
332 let result2 = check_profile4(&config, &mut check_state, &buf2[..len2]);
333 assert_eq!(result2.status, E2ECheckStatus::Ok);
334
335 let result3 = check_profile4(&config, &mut check_state, &buf1[..len1]);
337 assert!(matches!(
338 result3.status,
339 E2ECheckStatus::Repeated | E2ECheckStatus::WrongSequence
340 ));
341 }
342
343 #[test]
344 fn test_profile4_some_lost_detection() {
345 let config = Profile4Config::new(0x1234_5678, 5);
346 let mut protect_state = Profile4State::new();
347 let mut check_state = Profile4State::new();
348
349 let payload = b"Test";
350 let mut buf = [0u8; 256];
351
352 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
354 let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
355 assert_eq!(result1.status, E2ECheckStatus::Ok);
356
357 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
359 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
360 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
361
362 let result4 = check_profile4(&config, &mut check_state, &buf[..len]);
364 assert_eq!(result4.status, E2ECheckStatus::OkSomeLost);
365 }
366
367 #[test]
368 fn test_profile4_wrong_sequence_detection() {
369 let config = Profile4Config::new(0x1234_5678, 2);
370 let mut protect_state = Profile4State::new();
371 let mut check_state = Profile4State::new();
372
373 let payload = b"Test";
374 let mut buf = [0u8; 256];
375
376 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
378 let result1 = check_profile4(&config, &mut check_state, &buf[..len]);
379 assert_eq!(result1.status, E2ECheckStatus::Ok);
380
381 for _ in 0..5 {
383 protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
384 }
385 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
386
387 let result = check_profile4(&config, &mut check_state, &buf[..len]);
389 assert_eq!(result.status, E2ECheckStatus::WrongSequence);
390 }
391
392 #[test]
393 fn test_profile4_crc_error() {
394 let config = Profile4Config::new(0x1234_5678, 15);
395 let mut protect_state = Profile4State::new();
396 let mut check_state = Profile4State::new();
397
398 let payload = b"Test";
399 let mut buf = [0u8; 256];
400 let len = protect_profile4(&config, &mut protect_state, payload, &mut buf).unwrap();
401
402 buf[8] ^= 0xFF;
404
405 let result = check_profile4(&config, &mut check_state, &buf[..len]);
406 assert_eq!(result.status, E2ECheckStatus::CrcError);
407 }
408
409 #[test]
410 fn test_profile5_crc_error() {
411 let config = Profile5Config::new(0x1234, 20, 15);
412 let mut protect_state = Profile5State::new();
413 let mut check_state = Profile5State::new();
414
415 let mut payload = [0u8; 20];
416 payload[..4].copy_from_slice(b"Test");
417 let mut buf = [0u8; 256];
418 let len = protect_profile5(&config, &mut protect_state, &payload, &mut buf).unwrap();
419
420 buf[1] ^= 0xFF;
422
423 let result = check_profile5(&config, &mut check_state, &buf[..len]);
424 assert_eq!(result.status, E2ECheckStatus::CrcError);
425 }
426
427 #[test]
428 fn test_profile4_bad_argument_short_message() {
429 let config = Profile4Config::new(0x1234_5678, 15);
430 let mut check_state = Profile4State::new();
431
432 let short_message = [0u8; 8];
434 let result = check_profile4(&config, &mut check_state, &short_message);
435 assert_eq!(result.status, E2ECheckStatus::BadArgument);
436 }
437
438 #[test]
439 fn test_profile5_bad_argument_short_message() {
440 let config = Profile5Config::new(0x1234, 20, 15);
441 let mut check_state = Profile5State::new();
442
443 let short_message = [0u8; 2];
445 let result = check_profile5(&config, &mut check_state, &short_message);
446 assert_eq!(result.status, E2ECheckStatus::BadArgument);
447 }
448
449 #[cfg(feature = "std")]
450 #[test]
451 fn test_check_result_to_owned_payload() {
452 let data = b"hello";
453 let result = E2ECheckResult::success(E2ECheckStatus::Ok, 0, data);
454 let owned = result.to_owned_payload();
455 assert_eq!(owned, Some(b"hello".to_vec()));
456
457 let err_result = E2ECheckResult::error(E2ECheckStatus::CrcError);
458 assert_eq!(err_result.to_owned_payload(), None);
459 }
460
461 #[test]
462 fn test_e2e_key_from_message_id() {
463 let mid = crate::protocol::MessageId::new_from_service_and_method(0x1234, 0x0001);
464 let key = E2EKey::from_message_id(mid);
465 assert_eq!(key.service_id, 0x1234);
466 assert_eq!(key.method_or_event_id, 0x0001);
467 }
468}