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