1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod base58;
44mod builder;
45mod error;
46pub mod options;
47mod packet;
48#[cfg(feature = "region-codec")]
49mod region;
50
51pub use builder::{
52 BlindUnicastBuilder, BroadcastBuilder, MacAckBuilder, MulticastBuilder, PacketBuilder,
53 UnicastBuilder, state,
54};
55pub use error::{AddressParseError, BuildError, EncodeError, ParseError};
56pub use packet::{
57 ChannelId, ChannelKey, ChannelTag, Fcf, FloodHops, MicSize, NodeHint, OptionNumber,
58 PacketHeader, PacketType, ParsedOptions, PayloadType, PublicKey, RouterHint, Scf, SecInfo,
59 SourceAddr, SourceAddrRef, UMSH_VERSION, UnsealedPacket, feed_aad, iter_options,
60};
61#[cfg(feature = "region-codec")]
62pub use region::{RegionCode, RegionCodeError};
63
64#[cfg(test)]
65mod tests {
66 use crate::{
67 Fcf, MicSize, NodeHint, OptionNumber, PacketBuilder, PacketHeader, PacketType, PublicKey,
68 Scf, SecInfo, SourceAddrRef, feed_aad,
69 options::{OptionDecoder, OptionEncoder},
70 };
71
72 #[test]
73 fn option_codec_round_trip() {
74 let mut buf = [0u8; 32];
75 let mut enc = OptionEncoder::new(&mut buf);
76 enc.put(1, &[0x78, 0x53]).unwrap();
77 enc.put(2, &[]).unwrap();
78 enc.end_marker().unwrap();
79 let len = enc.finish();
80
81 let mut decoder = OptionDecoder::new(&buf[..len]);
82 assert_eq!(decoder.next().unwrap().unwrap(), (1, &[0x78, 0x53][..]));
83 assert_eq!(decoder.next().unwrap().unwrap(), (2, &[][..]));
84 assert!(decoder.next().is_none());
85 }
86
87 #[test]
88 fn secinfo_round_trip() {
89 let sec = SecInfo {
90 scf: Scf::new(true, MicSize::Mic16, true),
91 frame_counter: 42,
92 salt: Some(0x1234),
93 };
94 let mut buf = [0u8; 7];
95 let len = sec.encode(&mut buf).unwrap();
96 assert_eq!(len, 7);
97 assert_eq!(SecInfo::decode(&buf).unwrap(), sec);
98 }
99
100 #[test]
101 fn parse_broadcast_beacon() {
102 let bytes = [0xC0, 0xA1, 0xB2, 0x03];
103 let header = PacketHeader::parse(&bytes).unwrap();
104 assert_eq!(header.packet_type(), PacketType::Broadcast);
105 assert!(header.is_beacon());
106 }
107
108 #[test]
109 fn empty_payload_emits_no_end_of_options_marker() {
110 let mut buf = [0u8; 64];
114 let beacon = PacketBuilder::new(&mut buf)
115 .broadcast()
116 .source_hint(NodeHint([0x8B, 0x60, 0xF5]))
117 .flood_hops(5)
118 .payload(&[])
119 .build()
120 .unwrap();
121 assert_eq!(beacon, &[0xC1, 0x50, 0x8B, 0x60, 0xF5]);
122 let header = PacketHeader::parse(beacon).unwrap();
123 assert!(header.is_beacon());
124
125 let mut buf = [0u8; 64];
127 let with_payload = PacketBuilder::new(&mut buf)
128 .broadcast()
129 .source_hint(NodeHint([0x8B, 0x60, 0xF5]))
130 .payload(b"hi")
131 .build()
132 .unwrap();
133 assert_eq!(with_payload, &[0xC0, 0x8B, 0x60, 0xF5, 0xFF, b'h', b'i']);
134
135 let mut buf = [0u8; 128];
137 let unicast = PacketBuilder::new(&mut buf)
138 .unicast(NodeHint([0xC3, 0xD4, 0x25]))
139 .source_hint(NodeHint([0xA1, 0xB2, 0x03]))
140 .frame_counter(7)
141 .encrypted()
142 .mic_size(MicSize::Mic8)
143 .payload(&[])
144 .build()
145 .unwrap();
146 let bytes = unicast.as_bytes();
148 assert_eq!(
149 bytes,
150 &[
151 0xD0, 0xC3, 0xD4, 0x25, 0xA1, 0xB2, 0x03, 0xA0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 ],
154 );
155 let header = PacketHeader::parse(bytes).unwrap();
156 assert!(header.options_range.is_empty());
157 assert!(header.body_range.is_empty());
158 assert_eq!(header.mic_range.len(), 8);
159 }
160
161 #[test]
162 fn builder_and_parser_for_unicast_match() {
163 let mut buf = [0u8; 128];
164 let src = PublicKey([0xA1; 32]);
165 let dst = NodeHint([0xC3, 0xD4, 0x25]);
166 let packet = PacketBuilder::new(&mut buf)
167 .unicast(dst)
168 .source_full(&src)
169 .frame_counter(42)
170 .encrypted()
171 .mic_size(MicSize::Mic16)
172 .payload(b"hello")
173 .build()
174 .unwrap();
175
176 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
177 assert_eq!(header.packet_type(), PacketType::Unicast);
178 assert_eq!(header.dst, Some(dst));
179 assert_eq!(header.body_range.len(), 5);
180 }
181
182 #[test]
183 fn blind_unicast_builder_and_parser_match() {
184 let mut buf = [0u8; 128];
185 let src = PublicKey([0xA1; 32]);
186 let dst = NodeHint([0xC3, 0xD4, 0x25]);
187 let channel = crate::ChannelId([0x7E, 0x5F]);
188 let packet = PacketBuilder::new(&mut buf)
189 .blind_unicast(channel, dst)
190 .source_full(&src)
191 .frame_counter(5)
192 .payload(b"hello")
193 .build()
194 .unwrap();
195
196 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
197 assert_eq!(header.packet_type(), PacketType::BlindUnicast);
198 assert_eq!(header.channel, Some(channel));
199 assert_eq!(packet.blind_addr().unwrap().len(), 35);
200 assert_eq!(header.body_range.len(), 5);
201 }
202
203 #[test]
204 fn unencrypted_blind_unicast_builder_and_parser_match() {
205 let mut buf = [0u8; 128];
206 let src = PublicKey([0xA1; 32]);
207 let dst = NodeHint([0xC3, 0xD4, 0x25]);
208 let channel = crate::ChannelId([0x7E, 0x5F]);
209 let packet = PacketBuilder::new(&mut buf)
210 .blind_unicast(channel, dst)
211 .source_full(&src)
212 .frame_counter(5)
213 .unencrypted()
214 .payload(b"hello")
215 .build()
216 .unwrap();
217
218 let header = PacketHeader::parse(packet.as_bytes()).unwrap();
219 assert_eq!(header.packet_type(), PacketType::BlindUnicast);
220 assert_eq!(header.channel, Some(channel));
221 assert_eq!(header.dst, Some(dst));
222 assert_eq!(
223 header.source,
224 SourceAddrRef::FullKeyAt {
225 offset: header.body_range.start - 32
226 }
227 );
228 assert!(!header.sec_info.unwrap().scf.encrypted());
229 assert_eq!(header.body_range.len(), 5);
230 }
231
232 #[test]
233 fn builder_encodes_incremental_options_with_correct_deltas() {
234 let mut buf = [0u8; 128];
235 let src = NodeHint([0xA1, 0xB2, 0x03]);
236 let dst = NodeHint([0xC3, 0xD4, 0x25]);
237 let packet = PacketBuilder::new(&mut buf)
238 .unicast(dst)
239 .source_hint(src)
240 .frame_counter(10)
241 .encrypted()
242 .trace_route()
243 .region_code([0x78, 0x53])
244 .payload(b"hey")
245 .build()
246 .unwrap();
247
248 assert_eq!(&packet.as_bytes()[12..17], &[0x20, 0x92, 0x78, 0x53, 0xFF]);
251 }
252
253 #[test]
254 fn aad_excludes_dynamic_options() {
255 let mut bytes = [0u8; 64];
260 bytes[0] = Fcf::new(PacketType::Unicast, false, false).0;
261 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;
268 bytes[15] = 0x53;
269 bytes[16] = 0xFF; bytes[17..20].copy_from_slice(b"hey"); bytes[20..28].fill(0x11); let header = PacketHeader::parse(&bytes[..28]).unwrap();
273 let mut aad = [0u8; 18];
274 let mut aad_len = 0usize;
275 feed_aad(&header, &bytes[..28], |chunk| {
276 let next_len = aad_len + chunk.len();
277 aad[aad_len..next_len].copy_from_slice(chunk);
278 aad_len = next_len;
279 });
280 assert_eq!(
281 &aad[..aad_len],
282 &[
283 bytes[0],
284 0xC3,
285 0xD4,
286 0x25,
287 0xA1,
288 0xB2,
289 0x03,
290 Scf::new(true, MicSize::Mic8, false).0,
291 0x00,
292 0x00,
293 0x00,
294 0x2A,
295 ]
296 );
297 }
298
299 #[test]
300 fn aad_encodes_static_option_tl_as_u16_be_pairs() {
301 let mut buf = [0u8; 96];
302 let packet = PacketBuilder::new(&mut buf)
303 .unicast(NodeHint([0xC3, 0xD4, 0x25]))
304 .source_hint(NodeHint([0xA1, 0xB2, 0x03]))
305 .frame_counter(42)
306 .encrypted()
307 .option(OptionNumber::Unknown(300), &[0xAA])
308 .payload(b"hey")
309 .build()
310 .unwrap();
311 let bytes = packet.as_bytes().to_vec();
312 let header = PacketHeader::parse(&bytes).unwrap();
313 let mut aad = [0u8; 32];
314 let mut aad_len = 0usize;
315
316 feed_aad(&header, &bytes, |chunk| {
317 let next_len = aad_len + chunk.len();
318 aad[aad_len..next_len].copy_from_slice(chunk);
319 aad_len = next_len;
320 });
321
322 assert_eq!(&aad[1..6], &[0x01, 0x2C, 0x00, 0x01, 0xAA]);
323 }
324
325 #[test]
326 fn parse_blind_unicast_tracks_secinfo_range() {
327 let bytes = [
330 0xF0, 0x7E, 0x5F, 0x80, 0x00, 0x00, 0x00, 0x05, 0xFF, 0xC3, 0xD4, 0x25, 0xA1, 0xB2,
331 0x03, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x11, 0x22, 0x33, 0x44,
332 ];
333 let header = PacketHeader::parse(&bytes).unwrap();
334 assert_eq!(header.sec_info.unwrap().wire_len(), 5);
335 }
336}