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()[1..6], &[0x20, 0x92, 0x78, 0x53, 0xFF]);
191 }
192
193 #[test]
194 fn aad_excludes_dynamic_options() {
195 let mut bytes = [0u8; 64];
196 bytes[0] = Fcf::new(PacketType::Unicast, false, true, false).0;
197 bytes[1] = 0x20;
198 bytes[2] = 0x92;
199 bytes[3] = 0x78;
200 bytes[4] = 0x53;
201 bytes[5] = 0xFF;
202 bytes[6..9].copy_from_slice(&[0xC3, 0xD4, 0x25]);
203 bytes[9..12].copy_from_slice(&[0xA1, 0xB2, 0x03]);
204 bytes[12] = Scf::new(true, MicSize::Mic8, false).0;
205 bytes[13..17].copy_from_slice(&42u32.to_be_bytes());
206 bytes[17..20].copy_from_slice(b"hey");
207 bytes[20..28].fill(0x11);
208 let header = PacketHeader::parse(&bytes[..28]).unwrap();
209 let mut aad = [0u8; 18];
210 let mut aad_len = 0usize;
211 feed_aad(&header, &bytes[..28], |chunk| {
212 let next_len = aad_len + chunk.len();
213 aad[aad_len..next_len].copy_from_slice(chunk);
214 aad_len = next_len;
215 });
216 assert_eq!(
217 &aad[..aad_len],
218 &[
219 bytes[0],
220 0xC3,
221 0xD4,
222 0x25,
223 0xA1,
224 0xB2,
225 0x03,
226 Scf::new(true, MicSize::Mic8, false).0,
227 0x00,
228 0x00,
229 0x00,
230 0x2A,
231 ]
232 );
233 }
234
235 #[test]
236 fn aad_encodes_static_option_tl_as_u16_be_pairs() {
237 let mut buf = [0u8; 96];
238 let packet = PacketBuilder::new(&mut buf)
239 .unicast(NodeHint([0xC3, 0xD4, 0x25]))
240 .source_hint(NodeHint([0xA1, 0xB2, 0x03]))
241 .frame_counter(42)
242 .encrypted()
243 .option(OptionNumber::Unknown(300), &[0xAA])
244 .payload(b"hey")
245 .build()
246 .unwrap();
247 let bytes = packet.as_bytes().to_vec();
248 let header = PacketHeader::parse(&bytes).unwrap();
249 let mut aad = [0u8; 32];
250 let mut aad_len = 0usize;
251
252 feed_aad(&header, &bytes, |chunk| {
253 let next_len = aad_len + chunk.len();
254 aad[aad_len..next_len].copy_from_slice(chunk);
255 aad_len = next_len;
256 });
257
258 assert_eq!(&aad[1..6], &[0x01, 0x2C, 0x00, 0x01, 0xAA]);
259 }
260
261 #[test]
262 fn parse_blind_unicast_tracks_secinfo_range() {
263 let bytes = [
264 0xF0, 0x7E, 0x5F, 0x80, 0x00, 0x00, 0x00, 0x05, 0xC3, 0xD4, 0x25, 0xA1, 0xB2, 0x03,
265 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x11, 0x22, 0x33, 0x44,
266 ];
267 let header = PacketHeader::parse(&bytes).unwrap();
268 assert_eq!(header.sec_info.unwrap().wire_len(), 5);
269 }
270}