1#![cfg_attr(not(feature = "std"), no_std)]
2
3mod builder;
44mod error;
45pub mod options;
46mod packet;
47
48pub use builder::{
49 BlindUnicastBuilder, BroadcastBuilder, MacAckBuilder, MulticastBuilder, PacketBuilder,
50 UnicastBuilder, state,
51};
52pub use error::{BuildError, EncodeError, ParseError};
53pub use packet::{
54 ChannelId, ChannelKey, Fcf, FloodHops, MicSize, NodeHint, OptionNumber, PacketHeader,
55 PacketType, ParsedOptions, PayloadType, PublicKey, RouterHint, Scf, SecInfo, SourceAddr,
56 SourceAddrRef, UMSH_VERSION, UnsealedPacket, feed_aad, iter_options,
57};
58
59#[cfg(test)]
60mod tests {
61 use crate::{
62 Fcf, MicSize, NodeHint, OptionNumber, PacketBuilder, PacketHeader, PacketType, PublicKey,
63 Scf, SecInfo, SourceAddrRef, feed_aad,
64 options::{OptionDecoder, OptionEncoder},
65 };
66
67 #[test]
68 fn option_codec_round_trip() {
69 let mut buf = [0u8; 32];
70 let mut enc = OptionEncoder::new(&mut buf);
71 enc.put(1, &[0x78, 0x53]).unwrap();
72 enc.put(2, &[]).unwrap();
73 enc.end_marker().unwrap();
74 let len = enc.finish();
75
76 let mut decoder = OptionDecoder::new(&buf[..len]);
77 assert_eq!(decoder.next().unwrap().unwrap(), (1, &[0x78, 0x53][..]));
78 assert_eq!(decoder.next().unwrap().unwrap(), (2, &[][..]));
79 assert!(decoder.next().is_none());
80 }
81
82 #[test]
83 fn secinfo_round_trip() {
84 let sec = SecInfo {
85 scf: Scf::new(true, MicSize::Mic16, true),
86 frame_counter: 42,
87 salt: Some(0x1234),
88 };
89 let mut buf = [0u8; 7];
90 let len = sec.encode(&mut buf).unwrap();
91 assert_eq!(len, 7);
92 assert_eq!(SecInfo::decode(&buf).unwrap(), sec);
93 }
94
95 #[test]
96 fn parse_broadcast_beacon() {
97 let bytes = [0xC0, 0xA1, 0xB2, 0x03];
98 let header = PacketHeader::parse(&bytes).unwrap();
99 assert_eq!(header.packet_type(), PacketType::Broadcast);
100 assert!(header.is_beacon());
101 }
102
103 #[test]
104 fn builder_and_parser_for_unicast_match() {
105 let mut buf = [0u8; 128];
106 let src = PublicKey([0xA1; 32]);
107 let dst = NodeHint([0xC3, 0xD4, 0x25]);
108 let packet = PacketBuilder::new(&mut buf)
109 .unicast(dst)
110 .source_full(&src)
111 .frame_counter(42)
112 .encrypted()
113 .mic_size(MicSize::Mic16)
114 .payload(b"hello")
115 .build()
116 .unwrap();
117
118 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
119 assert_eq!(header.packet_type(), PacketType::Unicast);
120 assert_eq!(header.dst, Some(dst));
121 assert_eq!(header.body_range.len(), 5);
122 }
123
124 #[test]
125 fn blind_unicast_builder_and_parser_match() {
126 let mut buf = [0u8; 128];
127 let src = PublicKey([0xA1; 32]);
128 let dst = NodeHint([0xC3, 0xD4, 0x25]);
129 let channel = crate::ChannelId([0x7E, 0x5F]);
130 let packet = PacketBuilder::new(&mut buf)
131 .blind_unicast(channel, dst)
132 .source_full(&src)
133 .frame_counter(5)
134 .payload(b"hello")
135 .build()
136 .unwrap();
137
138 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
139 assert_eq!(header.packet_type(), PacketType::BlindUnicast);
140 assert_eq!(header.channel, Some(channel));
141 assert_eq!(packet.blind_addr().unwrap().len(), 35);
142 assert_eq!(header.body_range.len(), 5);
143 }
144
145 #[test]
146 fn unencrypted_blind_unicast_builder_and_parser_match() {
147 let mut buf = [0u8; 128];
148 let src = PublicKey([0xA1; 32]);
149 let dst = NodeHint([0xC3, 0xD4, 0x25]);
150 let channel = crate::ChannelId([0x7E, 0x5F]);
151 let packet = PacketBuilder::new(&mut buf)
152 .blind_unicast(channel, dst)
153 .source_full(&src)
154 .frame_counter(5)
155 .unencrypted()
156 .payload(b"hello")
157 .build()
158 .unwrap();
159
160 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
161 assert_eq!(header.packet_type(), PacketType::BlindUnicast);
162 assert_eq!(header.channel, Some(channel));
163 assert_eq!(header.dst, Some(dst));
164 assert_eq!(
165 header.source,
166 SourceAddrRef::FullKeyAt {
167 offset: header.body_range.start - 32
168 }
169 );
170 assert!(!header.sec_info.unwrap().scf.encrypted());
171 assert_eq!(header.body_range.len(), 5);
172 }
173
174 #[test]
175 fn builder_encodes_incremental_options_with_correct_deltas() {
176 let mut buf = [0u8; 128];
177 let src = NodeHint([0xA1, 0xB2, 0x03]);
178 let dst = NodeHint([0xC3, 0xD4, 0x25]);
179 let packet = PacketBuilder::new(&mut buf)
180 .unicast(dst)
181 .source_hint(src)
182 .frame_counter(10)
183 .encrypted()
184 .trace_route()
185 .region_code([0x78, 0x53])
186 .payload(b"hey")
187 .build()
188 .unwrap();
189
190 assert_eq!(&packet.as_bytes()[12..17], &[0x20, 0x92, 0x78, 0x53, 0xFF]);
193 }
194
195 #[test]
196 fn aad_excludes_dynamic_options() {
197 let mut bytes = [0u8; 64];
202 bytes[0] = Fcf::new(PacketType::Unicast, false, false).0;
203 bytes[1..4].copy_from_slice(&[0xC3, 0xD4, 0x25]); bytes[4..7].copy_from_slice(&[0xA1, 0xB2, 0x03]); bytes[7] = Scf::new(true, MicSize::Mic8, false).0; bytes[8..12].copy_from_slice(&42u32.to_be_bytes()); bytes[12] = 0x20; bytes[13] = 0x92; bytes[14] = 0x78;
210 bytes[15] = 0x53;
211 bytes[16] = 0xFF; bytes[17..20].copy_from_slice(b"hey"); bytes[20..28].fill(0x11); let header = PacketHeader::parse(&bytes[..28]).unwrap();
215 let mut aad = [0u8; 18];
216 let mut aad_len = 0usize;
217 feed_aad(&header, &bytes[..28], |chunk| {
218 let next_len = aad_len + chunk.len();
219 aad[aad_len..next_len].copy_from_slice(chunk);
220 aad_len = next_len;
221 });
222 assert_eq!(
223 &aad[..aad_len],
224 &[
225 bytes[0],
226 0xC3,
227 0xD4,
228 0x25,
229 0xA1,
230 0xB2,
231 0x03,
232 Scf::new(true, MicSize::Mic8, false).0,
233 0x00,
234 0x00,
235 0x00,
236 0x2A,
237 ]
238 );
239 }
240
241 #[test]
242 fn aad_encodes_static_option_tl_as_u16_be_pairs() {
243 let mut buf = [0u8; 96];
244 let packet = PacketBuilder::new(&mut buf)
245 .unicast(NodeHint([0xC3, 0xD4, 0x25]))
246 .source_hint(NodeHint([0xA1, 0xB2, 0x03]))
247 .frame_counter(42)
248 .encrypted()
249 .option(OptionNumber::Unknown(300), &[0xAA])
250 .payload(b"hey")
251 .build()
252 .unwrap();
253 let bytes = packet.as_bytes().to_vec();
254 let header = PacketHeader::parse(&bytes).unwrap();
255 let mut aad = [0u8; 32];
256 let mut aad_len = 0usize;
257
258 feed_aad(&header, &bytes, |chunk| {
259 let next_len = aad_len + chunk.len();
260 aad[aad_len..next_len].copy_from_slice(chunk);
261 aad_len = next_len;
262 });
263
264 assert_eq!(&aad[1..6], &[0x01, 0x2C, 0x00, 0x01, 0xAA]);
265 }
266
267 #[test]
268 fn parse_blind_unicast_tracks_secinfo_range() {
269 let bytes = [
272 0xF0, 0x7E, 0x5F, 0x80, 0x00, 0x00, 0x00, 0x05, 0xFF, 0xC3, 0xD4, 0x25, 0xA1, 0xB2,
273 0x03, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x11, 0x22, 0x33, 0x44,
274 ];
275 let header = PacketHeader::parse(&bytes).unwrap();
276 assert_eq!(header.sec_info.unwrap().wire_len(), 5);
277 }
278}