1use super::Error;
9
10#[derive(Debug, Copy, Clone, Eq, PartialEq)]
12#[repr(u8)]
13pub enum ServiceGroup {
14 KeyManagement = 0b00,
16 SaManagementIr = 0b01,
18 SaManagementRi = 0b10,
20 MonitoringControl = 0b11,
22}
23
24impl TryFrom<u8> for ServiceGroup {
25 type Error = Error;
26
27 fn try_from(value: u8) -> Result<Self, Self::Error> {
28 match value {
29 0b00 => Ok(Self::KeyManagement),
30 0b01 => Ok(Self::SaManagementIr),
31 0b10 => Ok(Self::SaManagementRi),
32 0b11 => Ok(Self::MonitoringControl),
33 _ => Err(Error::InvalidProcedure),
34 }
35 }
36}
37
38#[derive(Debug, Copy, Clone, Eq, PartialEq)]
40#[repr(u8)]
41pub enum KeyProcedure {
42 Otar = 0b0001,
44 Activate = 0b0010,
46 Deactivate = 0b0011,
48 Verify = 0b0100,
50 Destroy = 0b0110,
52 Inventory = 0b0111,
54}
55
56impl TryFrom<u8> for KeyProcedure {
57 type Error = Error;
58
59 fn try_from(value: u8) -> Result<Self, Self::Error> {
60 match value {
61 0b0001 => Ok(Self::Otar),
62 0b0010 => Ok(Self::Activate),
63 0b0011 => Ok(Self::Deactivate),
64 0b0100 => Ok(Self::Verify),
65 0b0110 => Ok(Self::Destroy),
66 0b0111 => Ok(Self::Inventory),
67 _ => Err(Error::InvalidProcedure),
68 }
69 }
70}
71
72#[derive(Debug, Copy, Clone, Eq, PartialEq)]
74#[repr(u8)]
75pub enum SaProcedure {
76 Create = 0b0001,
78 Delete = 0b0100,
80 Rekey = 0b0110,
82 Start = 0b1011,
84 Stop = 0b1110,
86 Expire = 0b1001,
88 SetArsn = 0b1010,
90 SetArsnWindow = 0b0101,
92 ReadArsn = 0b0000,
94 Status = 0b1111,
96}
97
98impl TryFrom<u8> for SaProcedure {
99 type Error = Error;
100
101 fn try_from(value: u8) -> Result<Self, Self::Error> {
102 match value {
103 0b0001 => Ok(Self::Create),
104 0b0100 => Ok(Self::Delete),
105 0b0110 => Ok(Self::Rekey),
106 0b1011 => Ok(Self::Start),
107 0b1110 => Ok(Self::Stop),
108 0b1001 => Ok(Self::Expire),
109 0b1010 => Ok(Self::SetArsn),
110 0b0101 => Ok(Self::SetArsnWindow),
111 0b0000 => Ok(Self::ReadArsn),
112 0b1111 => Ok(Self::Status),
113 _ => Err(Error::InvalidProcedure),
114 }
115 }
116}
117
118#[derive(Debug, Copy, Clone, Eq, PartialEq)]
120#[repr(u8)]
121pub enum McProcedure {
122 Ping = 0b0001,
124 LogStatus = 0b0010,
126 DumpLog = 0b0011,
128 EraseLog = 0b0100,
130 SelfTest = 0b0101,
132 ResetAlarmFlag = 0b0111,
134}
135
136impl TryFrom<u8> for McProcedure {
137 type Error = Error;
138
139 fn try_from(value: u8) -> Result<Self, Self::Error> {
140 match value {
141 0b0001 => Ok(Self::Ping),
142 0b0010 => Ok(Self::LogStatus),
143 0b0011 => Ok(Self::DumpLog),
144 0b0100 => Ok(Self::EraseLog),
145 0b0101 => Ok(Self::SelfTest),
146 0b0111 => Ok(Self::ResetAlarmFlag),
147 _ => Err(Error::InvalidProcedure),
148 }
149 }
150}
151
152#[derive(Debug, Copy, Clone, Eq, PartialEq)]
154pub enum PduType {
155 Command,
157 Reply,
159}
160
161use crate::utils::get_bits_u8;
162use crate::utils::set_bits_u8;
163
164#[rustfmt::skip]
165mod bitmask {
166 pub const TYPE_MASK: u8 = 0b_1000_0000;
168 pub const USER_FLAG_MASK: u8 = 0b_0100_0000;
170 pub const SERVICE_GROUP_MASK: u8 = 0b_0011_0000;
172 pub const PROCEDURE_ID_MASK: u8 = 0b_0000_1111;
174}
175
176pub const EP_HEADER_SIZE: usize = 3;
178
179#[derive(Debug, Copy, Clone)]
185pub struct EpHeader {
186 pub pdu_type: PduType,
188 pub user_flag: bool,
190 pub service_group: u8,
192 pub procedure_id: u8,
194}
195
196impl EpHeader {
197 pub fn parse(bytes: &[u8]) -> Result<(Self, u16), Error> {
201 if bytes.len() < EP_HEADER_SIZE {
202 return Err(Error::FrameTooShort);
203 }
204
205 let tag = bytes[0];
206 let pdu_type = match get_bits_u8(tag, bitmask::TYPE_MASK) {
207 0 => PduType::Command,
208 _ => PduType::Reply,
209 };
210 let user_flag = get_bits_u8(tag, bitmask::USER_FLAG_MASK) != 0;
211 let service_group = get_bits_u8(tag, bitmask::SERVICE_GROUP_MASK);
212 let procedure_id = get_bits_u8(tag, bitmask::PROCEDURE_ID_MASK);
213
214 let length_bits = u16::from_be_bytes([bytes[1], bytes[2]]);
215 let length_bytes = length_bits / 8;
216
217 Ok((
218 Self {
219 pdu_type,
220 user_flag,
221 service_group,
222 procedure_id,
223 },
224 length_bytes,
225 ))
226 }
227
228 pub fn encode(&self, data_len_bytes: u16, out: &mut [u8]) -> Result<usize, Error> {
233 if out.len() < EP_HEADER_SIZE {
234 return Err(Error::BufferTooSmall {
235 required: EP_HEADER_SIZE,
236 available: out.len(),
237 });
238 }
239
240 let mut tag = 0u8;
241 let type_val = match self.pdu_type {
242 PduType::Command => 0u8,
243 PduType::Reply => 1u8,
244 };
245 set_bits_u8(&mut tag, bitmask::TYPE_MASK, type_val);
246 set_bits_u8(&mut tag, bitmask::USER_FLAG_MASK, self.user_flag as u8);
247 set_bits_u8(&mut tag, bitmask::SERVICE_GROUP_MASK, self.service_group);
248 set_bits_u8(&mut tag, bitmask::PROCEDURE_ID_MASK, self.procedure_id);
249 out[0] = tag;
250
251 let length_bits = data_len_bytes * 8;
252 let len_bytes = length_bits.to_be_bytes();
253 out[1] = len_bytes[0];
254 out[2] = len_bytes[1];
255
256 Ok(EP_HEADER_SIZE)
257 }
258}
259
260#[derive(Debug, Copy, Clone)]
263pub struct FrameSecurityReport {
264 pub last_spi: u16,
266 pub arsn_lsb: u8,
268 pub alarm: bool,
270 pub bad_sn: bool,
272 pub bad_mac: bool,
274 pub bad_sa: bool,
276}
277
278impl FrameSecurityReport {
279 pub const VERSION: u8 = 0b100;
281
282 pub fn encode(&self, out: &mut [u8; 4]) {
284 let mut byte0: u8 = 0;
285 set_bits_u8(&mut byte0, 0b_1000_0000, 1);
286 set_bits_u8(&mut byte0, 0b_0111_0000, Self::VERSION);
287 set_bits_u8(&mut byte0, 0b_0000_1000, self.alarm as u8);
288 set_bits_u8(&mut byte0, 0b_0000_0100, self.bad_sn as u8);
289 set_bits_u8(&mut byte0, 0b_0000_0010, self.bad_mac as u8);
290 set_bits_u8(&mut byte0, 0b_0000_0001, self.bad_sa as u8);
291 out[0] = byte0;
292 let spi_bytes = self.last_spi.to_be_bytes();
293 out[1] = spi_bytes[0];
294 out[2] = spi_bytes[1];
295 out[3] = self.arsn_lsb;
296 }
297
298 pub fn parse(bytes: &[u8; 4]) -> Self {
300 Self {
301 alarm: get_bits_u8(bytes[0], 0b_0000_1000) != 0,
302 bad_sn: get_bits_u8(bytes[0], 0b_0000_0100) != 0,
303 bad_mac: get_bits_u8(bytes[0], 0b_0000_0010) != 0,
304 bad_sa: get_bits_u8(bytes[0], 0b_0000_0001) != 0,
305 last_spi: u16::from_be_bytes([bytes[1], bytes[2]]),
306 arsn_lsb: bytes[3],
307 }
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn header_roundtrip_key_otar() {
317 let hdr = EpHeader {
318 pdu_type: PduType::Command,
319 user_flag: false,
320 service_group: ServiceGroup::KeyManagement as u8,
321 procedure_id: KeyProcedure::Otar as u8,
322 };
323 let mut buf = [0u8; 8];
324 hdr.encode(10, &mut buf).unwrap();
325
326 let (parsed, length) = EpHeader::parse(&buf).unwrap();
327 assert_eq!(parsed.pdu_type, PduType::Command);
328 assert!(!parsed.user_flag);
329 assert_eq!(parsed.service_group, ServiceGroup::KeyManagement as u8);
330 assert_eq!(parsed.procedure_id, KeyProcedure::Otar as u8);
331 assert_eq!(length, 10);
332 }
333
334 #[test]
335 fn header_roundtrip_sa_start() {
336 let hdr = EpHeader {
337 pdu_type: PduType::Reply,
338 user_flag: true,
339 service_group: ServiceGroup::SaManagementIr as u8,
340 procedure_id: SaProcedure::Start as u8,
341 };
342 let mut buf = [0u8; 8];
343 hdr.encode(32, &mut buf).unwrap();
344
345 let (parsed, length) = EpHeader::parse(&buf).unwrap();
346 assert_eq!(parsed.pdu_type, PduType::Reply);
347 assert!(parsed.user_flag);
348 assert_eq!(parsed.service_group, ServiceGroup::SaManagementIr as u8);
349 assert_eq!(parsed.procedure_id, SaProcedure::Start as u8);
350 assert_eq!(length, 32);
351 }
352
353 #[test]
354 fn header_size_is_3_bytes() {
355 assert_eq!(EP_HEADER_SIZE, 3);
356 }
357
358 #[test]
359 fn service_group_try_from() {
360 assert_eq!(
361 ServiceGroup::try_from(0b00).unwrap(),
362 ServiceGroup::KeyManagement
363 );
364 assert_eq!(
365 ServiceGroup::try_from(0b01).unwrap(),
366 ServiceGroup::SaManagementIr
367 );
368 assert_eq!(
369 ServiceGroup::try_from(0b10).unwrap(),
370 ServiceGroup::SaManagementRi
371 );
372 assert_eq!(
373 ServiceGroup::try_from(0b11).unwrap(),
374 ServiceGroup::MonitoringControl
375 );
376 }
377
378 #[test]
379 fn mc_procedures() {
380 assert_eq!(McProcedure::try_from(0b0001).unwrap(), McProcedure::Ping);
381 assert_eq!(
382 McProcedure::try_from(0b0111).unwrap(),
383 McProcedure::ResetAlarmFlag
384 );
385 assert!(McProcedure::try_from(0b1111).is_err());
386 }
387
388 #[test]
389 fn buffer_too_short() {
390 assert!(EpHeader::parse(&[0u8; 2]).is_err());
391 }
392
393 #[test]
394 fn fsr_roundtrip() {
395 let fsr = FrameSecurityReport {
396 last_spi: 42,
397 arsn_lsb: 0xAB,
398 alarm: true,
399 bad_sn: false,
400 bad_mac: true,
401 bad_sa: false,
402 };
403 let mut buf = [0u8; 4];
404 fsr.encode(&mut buf);
405
406 let parsed = FrameSecurityReport::parse(&buf);
407 assert_eq!(parsed.last_spi, 42);
408 assert_eq!(parsed.arsn_lsb, 0xAB);
409 assert!(parsed.alarm);
410 assert!(!parsed.bad_sn);
411 assert!(parsed.bad_mac);
412 assert!(!parsed.bad_sa);
413 }
414
415 #[test]
416 fn fsr_control_word_type_is_1() {
417 let fsr = FrameSecurityReport {
418 last_spi: 0,
419 arsn_lsb: 0,
420 alarm: false,
421 bad_sn: false,
422 bad_mac: false,
423 bad_sa: false,
424 };
425 let mut buf = [0u8; 4];
426 fsr.encode(&mut buf);
427 assert_eq!(buf[0] & 0x80, 0x80);
428 }
429}