1use core::ops::Range;
2
3use crate::{EncodeError, ParseError, options::OptionDecoder};
4
5pub const UMSH_VERSION: u8 = 0b11;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10#[repr(u8)]
11pub enum PacketType {
12 Broadcast = 0,
13 MacAck = 1,
14 Unicast = 2,
15 UnicastAckReq = 3,
16 Multicast = 4,
17 Reserved5 = 5,
18 BlindUnicast = 6,
19 BlindUnicastAckReq = 7,
20}
21
22impl PacketType {
23 pub const fn from_bits(value: u8) -> Self {
25 match value & 0x07 {
26 0 => Self::Broadcast,
27 1 => Self::MacAck,
28 2 => Self::Unicast,
29 3 => Self::UnicastAckReq,
30 4 => Self::Multicast,
31 5 => Self::Reserved5,
32 6 => Self::BlindUnicast,
33 _ => Self::BlindUnicastAckReq,
34 }
35 }
36
37 pub fn is_secure(self) -> bool {
39 matches!(
40 self,
41 Self::Unicast
42 | Self::UnicastAckReq
43 | Self::Multicast
44 | Self::BlindUnicast
45 | Self::BlindUnicastAckReq
46 )
47 }
48
49 pub fn ack_requested(self) -> bool {
51 matches!(self, Self::UnicastAckReq | Self::BlindUnicastAckReq)
52 }
53
54 pub fn is_routable(self) -> bool {
56 !matches!(self, Self::Reserved5)
57 }
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66#[repr(u8)]
67pub enum PayloadType {
68 Empty = 0xFF,
70 Unspecified = 0,
72 NodeIdentity = 1,
74 MacCommand = 2,
76 TextMessage = 3,
78 ChatRoomMessage = 5,
80 CoapOverUmsh = 7,
82 NodeManagement = 8,
84}
85
86impl PayloadType {
87 pub fn from_byte(byte: u8) -> Option<Self> {
89 match byte {
90 0 => Some(Self::Unspecified),
91 1 => Some(Self::NodeIdentity),
92 2 => Some(Self::MacCommand),
93 3 => Some(Self::TextMessage),
94 5 => Some(Self::ChatRoomMessage),
95 7 => Some(Self::CoapOverUmsh),
96 8 => Some(Self::NodeManagement),
97 _ => None,
98 }
99 }
100
101 pub fn allowed_for(self, packet_type: PacketType) -> bool {
103 match self {
104 Self::Empty | Self::Unspecified | Self::NodeIdentity => {
105 !matches!(packet_type, PacketType::MacAck)
106 }
107 Self::MacCommand => matches!(
108 packet_type,
109 PacketType::Unicast
110 | PacketType::UnicastAckReq
111 | PacketType::BlindUnicast
112 | PacketType::BlindUnicastAckReq
113 | PacketType::Multicast
114 ),
115 Self::TextMessage | Self::CoapOverUmsh | Self::NodeManagement => matches!(
116 packet_type,
117 PacketType::Unicast
118 | PacketType::UnicastAckReq
119 | PacketType::BlindUnicast
120 | PacketType::BlindUnicastAckReq
121 | PacketType::Multicast
122 ),
123 Self::ChatRoomMessage => matches!(
124 packet_type,
125 PacketType::Unicast
126 | PacketType::UnicastAckReq
127 | PacketType::BlindUnicast
128 | PacketType::BlindUnicastAckReq
129 ),
130 }
131 }
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136pub struct Fcf(pub u8);
137
138impl Fcf {
139 pub const fn new(
141 packet_type: PacketType,
142 full_source: bool,
143 flood_hops_present: bool,
144 ) -> Self {
145 Self(
146 (UMSH_VERSION << 6)
147 | ((packet_type as u8) << 3)
148 | ((full_source as u8) << 2)
149 | flood_hops_present as u8,
150 )
151 }
152
153 pub const fn version(self) -> u8 {
155 self.0 >> 6
156 }
157
158 pub const fn packet_type(self) -> PacketType {
160 PacketType::from_bits((self.0 >> 3) & 0x07)
161 }
162
163 pub const fn full_source(self) -> bool {
165 self.0 & 0x04 != 0
166 }
167
168 pub const fn reserved_valid(self) -> bool {
170 self.0 & 0x02 == 0
171 }
172
173 pub const fn flood_hops_present(self) -> bool {
175 self.0 & 0x01 != 0
176 }
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub struct Scf(pub u8);
182
183impl Scf {
184 pub const fn new(encrypted: bool, mic_size: MicSize, salt_present: bool) -> Self {
186 Self(((encrypted as u8) << 7) | ((mic_size as u8) << 5) | ((salt_present as u8) << 4))
187 }
188
189 pub const fn encrypted(self) -> bool {
191 self.0 & 0x80 != 0
192 }
193
194 pub fn mic_size(self) -> Result<MicSize, ParseError> {
196 MicSize::from_bits((self.0 >> 5) & 0x03)
197 }
198
199 pub const fn salt_present(self) -> bool {
201 self.0 & 0x10 != 0
202 }
203
204 pub const fn reserved_valid(self) -> bool {
206 self.0 & 0x0F == 0
207 }
208}
209
210#[derive(Clone, Copy, Debug, PartialEq, Eq)]
212pub enum MicSize {
213 Mic4 = 0,
214 Mic8 = 1,
215 Mic12 = 2,
216 Mic16 = 3,
217}
218
219impl MicSize {
220 pub const fn byte_len(self) -> usize {
222 match self {
223 Self::Mic4 => 4,
224 Self::Mic8 => 8,
225 Self::Mic12 => 12,
226 Self::Mic16 => 16,
227 }
228 }
229
230 pub fn from_bits(value: u8) -> Result<Self, ParseError> {
232 match value {
233 0 => Ok(Self::Mic4),
234 1 => Ok(Self::Mic8),
235 2 => Ok(Self::Mic12),
236 3 => Ok(Self::Mic16),
237 other => Err(ParseError::InvalidMicSize(other)),
238 }
239 }
240}
241
242#[derive(Clone, Copy, Debug, PartialEq, Eq)]
244pub struct FloodHops(pub u8);
245
246impl FloodHops {
247 pub fn new(remaining: u8, accumulated: u8) -> Option<Self> {
249 if remaining <= 0x0F && accumulated <= 0x0F {
250 Some(Self((remaining << 4) | accumulated))
251 } else {
252 None
253 }
254 }
255
256 pub const fn remaining(self) -> u8 {
258 self.0 >> 4
259 }
260
261 pub const fn accumulated(self) -> u8 {
263 self.0 & 0x0F
264 }
265
266 pub fn decremented(self) -> Self {
268 let remaining = self.remaining();
269 if remaining == 0 {
270 self
271 } else {
272 Self::new(remaining - 1, self.accumulated().saturating_add(1)).unwrap_or(self)
273 }
274 }
275}
276
277#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
279pub struct NodeHint(pub [u8; 3]);
280
281impl NodeHint {
282 pub fn from_public_key(key: &PublicKey) -> Self {
284 Self([key.0[0], key.0[1], key.0[2]])
285 }
286}
287
288#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
290pub struct RouterHint(pub [u8; 2]);
291
292impl RouterHint {
293 pub fn from_public_key(key: &PublicKey) -> Self {
295 Self([key.0[0], key.0[1]])
296 }
297}
298
299#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
301pub struct ChannelId(pub [u8; 2]);
302
303#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
305pub struct PublicKey(pub [u8; 32]);
306
307impl PublicKey {
308 pub fn hint(&self) -> NodeHint {
310 NodeHint::from_public_key(self)
311 }
312
313 pub fn router_hint(&self) -> RouterHint {
315 RouterHint::from_public_key(self)
316 }
317}
318
319#[derive(Clone, Copy, zeroize::Zeroize)]
321pub struct ChannelKey(pub [u8; 32]);
322
323impl PartialEq for ChannelKey {
324 fn eq(&self, other: &Self) -> bool {
325 self.0 == other.0
326 }
327}
328
329impl Eq for ChannelKey {}
330
331impl core::fmt::Debug for ChannelKey {
332 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
333 f.write_str("ChannelKey([redacted])")
334 }
335}
336
337#[derive(Clone, Copy, Debug, PartialEq, Eq)]
339pub enum SourceAddr<'a> {
340 Hint(NodeHint),
341 Full(&'a PublicKey),
342}
343
344impl SourceAddr<'_> {
345 pub fn hint(&self) -> NodeHint {
347 match self {
348 Self::Hint(hint) => *hint,
349 Self::Full(key) => key.hint(),
350 }
351 }
352}
353
354#[derive(Clone, Copy, Debug, PartialEq, Eq)]
356pub struct SecInfo {
357 pub scf: Scf,
358 pub frame_counter: u32,
359 pub salt: Option<u16>,
360}
361
362impl SecInfo {
363 pub fn wire_len(&self) -> usize {
365 if self.salt.is_some() { 7 } else { 5 }
366 }
367
368 pub fn encode(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
370 let needed = self.wire_len();
371 if buf.len() < needed {
372 return Err(EncodeError::BufferTooSmall);
373 }
374 buf[0] = self.scf.0;
375 buf[1..5].copy_from_slice(&self.frame_counter.to_be_bytes());
376 if let Some(salt) = self.salt {
377 buf[5..7].copy_from_slice(&salt.to_be_bytes());
378 }
379 Ok(needed)
380 }
381
382 pub fn decode(buf: &[u8]) -> Result<Self, ParseError> {
384 if buf.len() < 5 {
385 return Err(ParseError::Truncated);
386 }
387 let scf = Scf(buf[0]);
388 if !scf.reserved_valid() {
389 return Err(ParseError::InvalidScfReserved);
390 }
391 let salt = if scf.salt_present() {
392 if buf.len() < 7 {
393 return Err(ParseError::Truncated);
394 }
395 Some(u16::from_be_bytes([buf[5], buf[6]]))
396 } else {
397 None
398 };
399 Ok(Self {
400 scf,
401 frame_counter: u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]),
402 salt,
403 })
404 }
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409pub enum OptionNumber {
410 RegionCode,
411 TraceRoute,
412 SourceRoute,
413 OperatorCallsign,
414 MinRssi,
415 RouteRetry,
416 StationCallsign,
417 MinSnr,
418 Unknown(u16),
419}
420
421impl OptionNumber {
422 pub fn as_u16(self) -> u16 {
424 match self {
425 Self::RegionCode => 11,
426 Self::TraceRoute => 2,
427 Self::SourceRoute => 3,
428 Self::OperatorCallsign => 4,
429 Self::MinRssi => 5,
430 Self::RouteRetry => 6,
431 Self::StationCallsign => 7,
432 Self::MinSnr => 9,
433 Self::Unknown(value) => value,
434 }
435 }
436
437 pub fn is_critical(self) -> bool {
439 self.as_u16() & 1 != 0
440 }
441
442 pub fn is_dynamic(self) -> bool {
444 self.as_u16() & 2 != 0
445 }
446}
447
448impl From<u16> for OptionNumber {
449 fn from(value: u16) -> Self {
450 match value {
451 2 => Self::TraceRoute,
452 3 => Self::SourceRoute,
453 4 => Self::OperatorCallsign,
454 5 => Self::MinRssi,
455 6 => Self::RouteRetry,
456 7 => Self::StationCallsign,
457 9 => Self::MinSnr,
458 11 => Self::RegionCode,
459 other => Self::Unknown(other),
460 }
461 }
462}
463
464#[derive(Clone, Copy, Debug, PartialEq, Eq)]
466pub enum SourceAddrRef {
467 Hint(NodeHint),
468 FullKeyAt { offset: usize },
469 Encrypted { offset: usize, len: usize },
470 None,
471}
472
473#[derive(Clone, Debug, PartialEq, Eq)]
475pub struct PacketHeader {
476 pub fcf: Fcf,
477 pub options_range: Range<usize>,
478 pub flood_hops: Option<FloodHops>,
479 pub dst: Option<NodeHint>,
480 pub channel: Option<ChannelId>,
481 pub ack_dst: Option<NodeHint>,
482 pub source: SourceAddrRef,
483 pub sec_info: Option<SecInfo>,
484 pub body_range: Range<usize>,
485 pub mic_range: Range<usize>,
486 pub total_len: usize,
487}
488
489impl PacketHeader {
490 pub fn parse(buf: &[u8]) -> Result<Self, ParseError> {
492 if buf.is_empty() {
493 return Err(ParseError::Truncated);
494 }
495
496 let fcf = Fcf(buf[0]);
497 if fcf.version() != UMSH_VERSION {
498 return Err(ParseError::InvalidVersion(fcf.version()));
499 }
500 if !fcf.reserved_valid() {
501 return Err(ParseError::InvalidFcfReserved);
502 }
503
504 let mut cursor = 1;
505 let flood_hops = if fcf.flood_hops_present() {
506 if cursor >= buf.len() {
507 return Err(ParseError::Truncated);
508 }
509 let fh = FloodHops(buf[cursor]);
510 cursor += 1;
511 Some(fh)
512 } else {
513 None
514 };
515
516 let packet_type = fcf.packet_type();
517 let mut dst = None;
518 let mut channel = None;
519 let mut ack_dst = None;
520 let mut source = SourceAddrRef::None;
521 let mut sec_info = None;
522
523 match packet_type {
524 PacketType::Broadcast => {
525 let src_len = source_len(fcf.full_source());
526 source = if fcf.full_source() {
527 ensure_len(buf, cursor, 32)?;
528 SourceAddrRef::FullKeyAt { offset: cursor }
529 } else {
530 ensure_len(buf, cursor, 3)?;
531 SourceAddrRef::Hint(NodeHint([buf[cursor], buf[cursor + 1], buf[cursor + 2]]))
532 };
533 cursor += src_len;
534 let options_start = cursor;
535 let options_end = buf.len();
536 let (consumed, has_marker) =
537 scan_options_bounded(&buf[options_start..options_end])?;
538 let options_range = options_start..options_start + consumed;
539 let body_start = options_start + consumed;
540 let body_end = if has_marker { buf.len() } else { body_start };
541 Ok(Self {
542 fcf,
543 options_range,
544 flood_hops,
545 dst,
546 channel,
547 ack_dst,
548 source,
549 sec_info,
550 body_range: body_start..body_end,
551 mic_range: buf.len()..buf.len(),
552 total_len: buf.len(),
553 })
554 }
555 PacketType::MacAck => {
556 ensure_len(buf, cursor, 3)?;
557 ack_dst = Some(NodeHint([buf[cursor], buf[cursor + 1], buf[cursor + 2]]));
558 cursor += 3;
559 let options_start = cursor;
560 let options_end = buf
561 .len()
562 .checked_sub(8)
563 .ok_or(ParseError::Truncated)?;
564 if options_end < options_start {
565 return Err(ParseError::Truncated);
566 }
567 let (consumed, _has_marker) =
568 scan_options_bounded(&buf[options_start..options_end])?;
569 let options_range = options_start..options_start + consumed;
570 let ack_tag_start = options_end;
571 Ok(Self {
572 fcf,
573 options_range,
574 flood_hops,
575 dst,
576 channel,
577 ack_dst,
578 source,
579 sec_info,
580 body_range: ack_tag_start..ack_tag_start + 8,
581 mic_range: ack_tag_start..ack_tag_start + 8,
582 total_len: ack_tag_start + 8,
583 })
584 }
585 PacketType::Unicast | PacketType::UnicastAckReq => {
586 ensure_len(buf, cursor, 3)?;
587 dst = Some(NodeHint([buf[cursor], buf[cursor + 1], buf[cursor + 2]]));
588 cursor += 3;
589 let src_len = source_len(fcf.full_source());
590 source = if fcf.full_source() {
591 ensure_len(buf, cursor, 32)?;
592 SourceAddrRef::FullKeyAt { offset: cursor }
593 } else {
594 ensure_len(buf, cursor, 3)?;
595 SourceAddrRef::Hint(NodeHint([buf[cursor], buf[cursor + 1], buf[cursor + 2]]))
596 };
597 cursor += src_len;
598 let parsed_sec = SecInfo::decode(&buf[cursor..])?;
599 let sec_len = parsed_sec.wire_len();
600 sec_info = Some(parsed_sec);
601 cursor += sec_len;
602 let mic_len = parsed_sec.scf.mic_size()?.byte_len();
603 let mic_start = buf
604 .len()
605 .checked_sub(mic_len)
606 .ok_or(ParseError::Truncated)?;
607 if mic_start < cursor {
608 return Err(ParseError::Truncated);
609 }
610 let options_start = cursor;
611 let (consumed, has_marker) =
612 scan_options_bounded(&buf[options_start..mic_start])?;
613 let options_range = options_start..options_start + consumed;
614 let body_start = options_start + consumed;
615 let body_end = if has_marker { mic_start } else { body_start };
616 Ok(Self {
617 fcf,
618 options_range,
619 flood_hops,
620 dst,
621 channel,
622 ack_dst,
623 source,
624 sec_info,
625 body_range: body_start..body_end,
626 mic_range: mic_start..buf.len(),
627 total_len: buf.len(),
628 })
629 }
630 PacketType::Multicast => {
631 ensure_len(buf, cursor, 2)?;
632 channel = Some(ChannelId([buf[cursor], buf[cursor + 1]]));
633 cursor += 2;
634 let parsed_sec = SecInfo::decode(&buf[cursor..])?;
635 let sec_len = parsed_sec.wire_len();
636 sec_info = Some(parsed_sec);
637 cursor += sec_len;
638 let mic_len = parsed_sec.scf.mic_size()?.byte_len();
639 let mic_start = buf
640 .len()
641 .checked_sub(mic_len)
642 .ok_or(ParseError::Truncated)?;
643 if mic_start < cursor {
644 return Err(ParseError::Truncated);
645 }
646 let options_start = cursor;
647 let (consumed, has_marker) =
648 scan_options_bounded(&buf[options_start..mic_start])?;
649 let options_range = options_start..options_start + consumed;
650 cursor = options_start + consumed;
651 if parsed_sec.scf.encrypted() {
652 let src_len = source_len(fcf.full_source());
653 source = SourceAddrRef::Encrypted {
654 offset: cursor,
655 len: src_len,
656 };
657 Ok(Self {
658 fcf,
659 options_range,
660 flood_hops,
661 dst,
662 channel,
663 ack_dst,
664 source,
665 sec_info,
666 body_range: cursor..mic_start,
667 mic_range: mic_start..buf.len(),
668 total_len: buf.len(),
669 })
670 } else {
671 let src_len = source_len(fcf.full_source());
672 source = if fcf.full_source() {
673 ensure_len(buf, cursor, 32)?;
674 SourceAddrRef::FullKeyAt { offset: cursor }
675 } else {
676 ensure_len(buf, cursor, 3)?;
677 SourceAddrRef::Hint(NodeHint([
678 buf[cursor],
679 buf[cursor + 1],
680 buf[cursor + 2],
681 ]))
682 };
683 cursor += src_len;
684 let body_start = cursor;
685 let body_end = if has_marker { mic_start } else { body_start };
686 Ok(Self {
687 fcf,
688 options_range,
689 flood_hops,
690 dst,
691 channel,
692 ack_dst,
693 source,
694 sec_info,
695 body_range: body_start..body_end,
696 mic_range: mic_start..buf.len(),
697 total_len: buf.len(),
698 })
699 }
700 }
701 PacketType::BlindUnicast | PacketType::BlindUnicastAckReq => {
702 ensure_len(buf, cursor, 2)?;
703 channel = Some(ChannelId([buf[cursor], buf[cursor + 1]]));
704 cursor += 2;
705 let parsed_sec = SecInfo::decode(&buf[cursor..])?;
706 let sec_len = parsed_sec.wire_len();
707 sec_info = Some(parsed_sec);
708 cursor += sec_len;
709 let mic_len = parsed_sec.scf.mic_size()?.byte_len();
710 let mic_start = buf
711 .len()
712 .checked_sub(mic_len)
713 .ok_or(ParseError::Truncated)?;
714 if mic_start < cursor {
715 return Err(ParseError::Truncated);
716 }
717 let options_start = cursor;
718 let (consumed, has_marker) =
719 scan_options_bounded(&buf[options_start..mic_start])?;
720 let options_range = options_start..options_start + consumed;
721 cursor = options_start + consumed;
722 let src_len = source_len(fcf.full_source());
723 ensure_len(buf, cursor, 3 + src_len)?;
724 if parsed_sec.scf.encrypted() {
725 source = SourceAddrRef::Encrypted {
726 offset: cursor + 3,
727 len: src_len,
728 };
729 cursor += 3 + src_len;
730 } else {
731 dst = Some(NodeHint([buf[cursor], buf[cursor + 1], buf[cursor + 2]]));
732 cursor += 3;
733 source = if fcf.full_source() {
734 ensure_len(buf, cursor, 32)?;
735 SourceAddrRef::FullKeyAt { offset: cursor }
736 } else {
737 ensure_len(buf, cursor, 3)?;
738 SourceAddrRef::Hint(NodeHint([
739 buf[cursor],
740 buf[cursor + 1],
741 buf[cursor + 2],
742 ]))
743 };
744 cursor += src_len;
745 }
746 let body_start = cursor;
747 let body_end = if has_marker { mic_start } else { body_start };
748 Ok(Self {
749 fcf,
750 options_range,
751 flood_hops,
752 dst,
753 channel,
754 ack_dst,
755 source,
756 sec_info,
757 body_range: body_start..body_end,
758 mic_range: mic_start..buf.len(),
759 total_len: buf.len(),
760 })
761 }
762 PacketType::Reserved5 => Err(ParseError::MalformedOption),
763 }
764 }
765
766 pub fn packet_type(&self) -> PacketType {
768 self.fcf.packet_type()
769 }
770
771 pub fn ack_requested(&self) -> bool {
773 self.packet_type().ack_requested()
774 }
775
776 pub fn is_beacon(&self) -> bool {
778 self.packet_type() == PacketType::Broadcast && self.body_range.is_empty()
779 }
780}
781
782#[derive(Clone, Debug, Default, PartialEq, Eq)]
783pub struct ParsedOptions {
784 pub region_code: Option<[u8; 2]>,
785 pub source_route: Option<Range<usize>>,
786 pub trace_route: Option<Range<usize>>,
787 pub min_rssi: Option<i16>,
788 pub min_snr: Option<i8>,
789 pub route_retry: bool,
790 pub has_unknown_critical: bool,
791}
792
793impl ParsedOptions {
794 pub fn extract(buf: &[u8], range: Range<usize>) -> Result<Self, ParseError> {
795 let mut parsed = Self::default();
796 if range.is_empty() {
797 return Ok(parsed);
798 }
799 let options = &buf[range.clone()];
800 for entry in OptionDecoder::new(options) {
801 let (number, value) = entry?;
802 let relative_start = unsafe { value.as_ptr().offset_from(options.as_ptr()) } as usize;
803 let value_start = range.start + relative_start;
804 let value_range = value_start..value_start + value.len();
805 match OptionNumber::from(number) {
806 OptionNumber::RegionCode if value.len() == 2 => {
807 parsed.region_code = Some([value[0], value[1]]);
808 }
809 OptionNumber::TraceRoute => parsed.trace_route = Some(value_range),
810 OptionNumber::SourceRoute => parsed.source_route = Some(value_range),
811 OptionNumber::RouteRetry if value.is_empty() => parsed.route_retry = true,
812 OptionNumber::MinRssi if value.len() == 2 => {
813 parsed.min_rssi = Some(i16::from_be_bytes([value[0], value[1]]));
814 }
815 OptionNumber::MinSnr if value.len() == 1 => parsed.min_snr = Some(value[0] as i8),
816 OptionNumber::Unknown(raw) if raw & 1 != 0 => parsed.has_unknown_critical = true,
817 _ => {}
818 }
819 }
820 Ok(parsed)
821 }
822}
823
824pub fn iter_options<'a>(buf: &'a [u8], range: Range<usize>) -> OptionDecoder<'a> {
825 OptionDecoder::new(&buf[range])
826}
827
828pub fn feed_aad(header: &PacketHeader, packet_buf: &[u8], mut sink: impl FnMut(&[u8])) {
829 sink(&packet_buf[..1]);
830 for option in iter_options(packet_buf, header.options_range.clone()) {
831 let Ok((number, value)) = option else {
832 return;
833 };
834 let option_number = OptionNumber::from(number);
835 if option_number.is_dynamic() {
836 continue;
837 }
838 let mut tl = [0u8; 4];
839 tl[..2].copy_from_slice(&number.to_be_bytes());
840 tl[2..].copy_from_slice(&(value.len() as u16).to_be_bytes());
841 sink(&tl);
842 sink(value);
843 }
844
845 if let Some(dst) = header.dst {
846 sink(&dst.0);
847 }
848 if let Some(channel) = header.channel {
849 sink(&channel.0);
850 }
851 match header.source {
852 SourceAddrRef::Hint(hint) => sink(&hint.0),
853 SourceAddrRef::FullKeyAt { offset } => sink(&packet_buf[offset..offset + 32]),
854 SourceAddrRef::Encrypted { .. } | SourceAddrRef::None => {}
855 }
856 if let Some(sec_info) = header.sec_info {
857 let mut buf = [0u8; 7];
858 let Ok(len) = sec_info.encode(&mut buf) else {
859 return;
860 };
861 sink(&buf[..len]);
862 }
863}
864
865fn ensure_len(buf: &[u8], offset: usize, len: usize) -> Result<(), ParseError> {
866 if buf.len() < offset + len {
867 Err(ParseError::Truncated)
868 } else {
869 Ok(())
870 }
871}
872
873fn scan_options_bounded(data: &[u8]) -> Result<(usize, bool), ParseError> {
880 let mut pos = 0;
881 let mut last_number: u16 = 0;
882 while pos < data.len() {
883 let first = data[pos];
884 if first == 0xFF {
885 return Ok((pos + 1, true));
886 }
887 pos += 1;
888 let delta_nibble = first >> 4;
889 let len_nibble = first & 0x0F;
890 let (delta, delta_len) = read_extended(&data[pos..], delta_nibble)?;
891 pos += delta_len;
892 let (len, len_len) = read_extended(&data[pos..], len_nibble)?;
893 pos += len_len;
894 if pos + len as usize > data.len() {
895 return Err(ParseError::Truncated);
896 }
897 let number = last_number
898 .checked_add(delta)
899 .ok_or(ParseError::MalformedOption)?;
900 pos += len as usize;
901 last_number = number;
902 }
903 Ok((pos, false))
904}
905
906fn read_extended(data: &[u8], nibble: u8) -> Result<(u16, usize), ParseError> {
907 match nibble {
908 0..=12 => Ok((nibble as u16, 0)),
909 13 => {
910 if data.is_empty() {
911 return Err(ParseError::Truncated);
912 }
913 Ok((data[0] as u16 + 13, 1))
914 }
915 14 => {
916 if data.len() < 2 {
917 return Err(ParseError::Truncated);
918 }
919 Ok((u16::from_be_bytes([data[0], data[1]]) + 269, 2))
920 }
921 _ => Err(ParseError::InvalidOptionNibble),
922 }
923}
924
925pub(crate) fn source_len(full_source: bool) -> usize {
926 if full_source { 32 } else { 3 }
927}
928
929#[derive(Debug, PartialEq, Eq)]
930pub struct UnsealedPacket<'a> {
931 buf: &'a mut [u8],
932 total_len: usize,
933 body_range: Range<usize>,
934 blind_addr_range: Option<Range<usize>>,
935 mic_range: Range<usize>,
936 sec_info_range: Range<usize>,
937 aad_static_options: Range<usize>,
938}
939
940impl<'a> UnsealedPacket<'a> {
941 pub fn new(
942 buf: &'a mut [u8],
943 total_len: usize,
944 body_range: Range<usize>,
945 blind_addr_range: Option<Range<usize>>,
946 mic_range: Range<usize>,
947 sec_info_range: Range<usize>,
948 aad_static_options: Range<usize>,
949 ) -> Self {
950 Self {
951 buf,
952 total_len,
953 body_range,
954 blind_addr_range,
955 mic_range,
956 sec_info_range,
957 aad_static_options,
958 }
959 }
960
961 pub fn header(&self) -> Result<PacketHeader, ParseError> {
962 PacketHeader::parse(self.as_bytes())
963 }
964
965 pub fn body(&self) -> &[u8] {
966 &self.buf[self.body_range.clone()]
967 }
968
969 pub fn body_mut(&mut self) -> &mut [u8] {
970 &mut self.buf[self.body_range.clone()]
971 }
972
973 pub fn blind_addr_range(&self) -> Option<Range<usize>> {
974 self.blind_addr_range.clone()
975 }
976
977 pub fn blind_addr(&self) -> Option<&[u8]> {
978 let range = self.blind_addr_range.clone()?;
979 Some(&self.buf[range])
980 }
981
982 pub fn mic_slot(&mut self) -> &mut [u8] {
983 &mut self.buf[self.mic_range.clone()]
984 }
985
986 pub fn as_bytes(&self) -> &[u8] {
987 &self.buf[..self.total_len]
988 }
989
990 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
991 &mut self.buf[..self.total_len]
992 }
993
994 pub fn total_len(&self) -> usize {
995 self.total_len
996 }
997
998 pub fn sec_info_range(&self) -> Range<usize> {
999 self.sec_info_range.clone()
1000 }
1001
1002 pub fn aad_static_options(&self) -> Range<usize> {
1003 self.aad_static_options.clone()
1004 }
1005}