1use crate::pui;
17
18pub const PUBLIC_KEY_LEN: usize = 32;
21pub const CHANNEL_KEY_LEN: usize = 32;
24pub const CHANNEL_ID_LEN: usize = 2;
27pub const REGION_CODE_LEN: usize = 2;
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum ItemError {
34 Truncated,
36 BadLength,
38 UnknownFilterType,
40 BufferTooSmall,
42 BadPrefix,
44}
45
46#[derive(Clone, Copy, PartialEq, Eq)]
54pub struct PeerKeyEntry {
55 pub public_key: [u8; PUBLIC_KEY_LEN],
56 pub k_enc: [u8; 16],
57 pub k_mic: [u8; 16],
58}
59
60impl PeerKeyEntry {
61 pub const WIRE_LEN: usize = 64;
62
63 pub fn encode(&self, out: &mut [u8]) -> Result<usize, ItemError> {
64 if out.len() < Self::WIRE_LEN {
65 return Err(ItemError::BufferTooSmall);
66 }
67 out[..32].copy_from_slice(&self.public_key);
68 out[32..48].copy_from_slice(&self.k_enc);
69 out[48..64].copy_from_slice(&self.k_mic);
70 Ok(Self::WIRE_LEN)
71 }
72
73 pub fn decode(input: &[u8]) -> Result<Self, ItemError> {
75 if input.len() != Self::WIRE_LEN {
76 return Err(ItemError::BadLength);
77 }
78 let mut entry = Self {
79 public_key: [0; PUBLIC_KEY_LEN],
80 k_enc: [0; 16],
81 k_mic: [0; 16],
82 };
83 entry.public_key.copy_from_slice(&input[..32]);
84 entry.k_enc.copy_from_slice(&input[32..48]);
85 entry.k_mic.copy_from_slice(&input[48..64]);
86 Ok(entry)
87 }
88
89 pub fn digest(&self) -> &[u8; PUBLIC_KEY_LEN] {
92 &self.public_key
93 }
94}
95
96impl core::fmt::Debug for PeerKeyEntry {
99 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100 formatter
101 .debug_struct("PeerKeyEntry")
102 .field("public_key", &self.public_key)
103 .finish_non_exhaustive()
104 }
105}
106
107pub const FILTER_DEST_HINT: u8 = 0;
109pub const FILTER_CHANNEL_ID: u8 = 1;
111pub const FILTER_PKT_TYPE: u8 = 2;
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
117pub enum Filter {
118 DestHint([u8; 3]),
120 ChannelId([u8; CHANNEL_ID_LEN]),
123 PktType(u8),
125}
126
127impl Filter {
128 pub const MAX_WIRE_LEN: usize = 4;
130
131 pub const fn wire_len(&self) -> usize {
132 match self {
133 Self::DestHint(_) => 4,
134 Self::ChannelId(_) => 3,
135 Self::PktType(_) => 2,
136 }
137 }
138
139 pub fn encode(&self, out: &mut [u8]) -> Result<usize, ItemError> {
140 let len = self.wire_len();
141 if out.len() < len {
142 return Err(ItemError::BufferTooSmall);
143 }
144 match self {
145 Self::DestHint(hint) => {
146 out[0] = FILTER_DEST_HINT;
147 out[1..4].copy_from_slice(hint);
148 }
149 Self::ChannelId(id) => {
150 out[0] = FILTER_CHANNEL_ID;
151 out[1..3].copy_from_slice(id);
152 }
153 Self::PktType(pkt_type) => {
154 out[0] = FILTER_PKT_TYPE;
155 out[1] = *pkt_type;
156 }
157 }
158 Ok(len)
159 }
160
161 pub fn decode(input: &[u8]) -> Result<Self, ItemError> {
167 let [filter_type, value @ ..] = input else {
168 return Err(ItemError::Truncated);
169 };
170 match (*filter_type, value) {
171 (FILTER_DEST_HINT, &[a, b, c]) => Ok(Self::DestHint([a, b, c])),
172 (FILTER_CHANNEL_ID, &[a, b]) => Ok(Self::ChannelId([a, b])),
173 (FILTER_PKT_TYPE, &[pkt_type]) => Ok(Self::PktType(pkt_type)),
174 (FILTER_DEST_HINT | FILTER_CHANNEL_ID | FILTER_PKT_TYPE, _) => {
175 Err(ItemError::BadLength)
176 }
177 _ => Err(ItemError::UnknownFilterType),
178 }
179 }
180}
181
182pub fn fixed_items<const N: usize>(
188 value: &[u8],
189) -> Result<impl ExactSizeIterator<Item = &[u8; N]> + Clone, ItemError> {
190 const { assert!(N > 0) };
191 if value.len() % N != 0 {
192 return Err(ItemError::BadLength);
193 }
194 Ok(value
195 .chunks_exact(N)
196 .map(|chunk| chunk.try_into().expect("chunks_exact yields N-byte chunks")))
197}
198
199pub fn prefixed_items(value: &[u8]) -> PrefixedItems<'_> {
206 PrefixedItems { rest: value }
207}
208
209#[derive(Clone)]
210pub struct PrefixedItems<'a> {
211 rest: &'a [u8],
212}
213
214impl<'a> Iterator for PrefixedItems<'a> {
215 type Item = Result<&'a [u8], ItemError>;
216
217 fn next(&mut self) -> Option<Self::Item> {
218 if self.rest.is_empty() {
219 return None;
220 }
221 let (len, consumed) = match pui::decode(self.rest) {
222 Ok(decoded) => decoded,
223 Err(_) => {
224 self.rest = &[];
225 return Some(Err(ItemError::BadPrefix));
226 }
227 };
228 let body = &self.rest[consumed..];
229 let Ok(len) = usize::try_from(len) else {
230 self.rest = &[];
231 return Some(Err(ItemError::BadPrefix));
232 };
233 if body.len() < len {
234 self.rest = &[];
235 return Some(Err(ItemError::Truncated));
236 }
237 let (item, rest) = body.split_at(len);
238 self.rest = rest;
239 Some(Ok(item))
240 }
241}
242
243pub fn encode_prefixed_item(item: &[u8], out: &mut [u8]) -> Result<usize, ItemError> {
246 let len = u32::try_from(item.len()).map_err(|_| ItemError::BadLength)?;
247 let prefix = pui::encode(len, out).map_err(|error| match error {
248 pui::Error::BufferTooSmall => ItemError::BufferTooSmall,
249 _ => ItemError::BadLength,
250 })?;
251 let end = prefix + item.len();
252 if out.len() < end {
253 return Err(ItemError::BufferTooSmall);
254 }
255 out[prefix..end].copy_from_slice(item);
256 Ok(end)
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn peer_key_entry_round_trip_and_digest_is_secret_free() {
265 let entry = PeerKeyEntry {
266 public_key: [0x11; 32],
267 k_enc: [0x22; 16],
268 k_mic: [0x33; 16],
269 };
270 let mut buf = [0u8; PeerKeyEntry::WIRE_LEN];
271 assert_eq!(entry.encode(&mut buf).unwrap(), PeerKeyEntry::WIRE_LEN);
272 assert_eq!(PeerKeyEntry::decode(&buf).unwrap(), entry);
273
274 assert_eq!(entry.digest(), &[0x11; 32]);
276 let debug = std::format!("{entry:?}");
278 assert!(
279 !debug.contains("k_enc") && !debug.contains("k_mic"),
280 "{debug}"
281 );
282 }
283
284 #[test]
285 fn peer_key_entry_rejects_wrong_lengths() {
286 assert_eq!(PeerKeyEntry::decode(&[0; 63]), Err(ItemError::BadLength));
287 assert_eq!(PeerKeyEntry::decode(&[0; 65]), Err(ItemError::BadLength));
288 assert_eq!(PeerKeyEntry::decode(&[]), Err(ItemError::BadLength));
289 }
290
291 #[test]
292 fn filter_round_trip_every_type() {
293 let filters = [
294 Filter::DestHint([0xAA, 0xBB, 0xCC]),
295 Filter::ChannelId([0x12, 0x34]),
296 Filter::PktType(0),
297 ];
298 for filter in filters {
299 let mut buf = [0u8; Filter::MAX_WIRE_LEN];
300 let len = filter.encode(&mut buf).unwrap();
301 assert_eq!(len, filter.wire_len());
302 assert_eq!(Filter::decode(&buf[..len]).unwrap(), filter);
303 }
304 }
305
306 #[test]
307 fn filter_rejects_malformed_entries() {
308 assert_eq!(Filter::decode(&[]), Err(ItemError::Truncated));
309 assert_eq!(
311 Filter::decode(&[FILTER_DEST_HINT, 1, 2]),
312 Err(ItemError::BadLength)
313 );
314 assert_eq!(
315 Filter::decode(&[FILTER_CHANNEL_ID, 1, 2, 3]),
316 Err(ItemError::BadLength)
317 );
318 assert_eq!(
319 Filter::decode(&[FILTER_PKT_TYPE]),
320 Err(ItemError::BadLength)
321 );
322 assert_eq!(Filter::decode(&[3, 0]), Err(ItemError::UnknownFilterType));
324 }
325
326 #[test]
327 fn fixed_items_iterates_and_validates_alignment() {
328 let value = [1u8, 1, 2, 2, 3, 3];
329 let items: Vec<[u8; 2]> = fixed_items::<2>(&value).unwrap().copied().collect();
330 assert_eq!(items, [[1, 1], [2, 2], [3, 3]]);
331
332 assert!(fixed_items::<2>(&[0; 5]).is_err());
333 assert_eq!(fixed_items::<32>(&[]).unwrap().len(), 0);
334 }
335
336 #[test]
337 fn prefixed_items_round_trip() {
338 let mut table = [0u8; 32];
339 let mut len = 0;
340 let items: [&[u8]; 3] = [&[0xAA, 0xBB], &[], &[0xCC; 5]];
341 for item in items {
342 len += encode_prefixed_item(item, &mut table[len..]).unwrap();
343 }
344 let decoded: Vec<&[u8]> = prefixed_items(&table[..len])
345 .collect::<Result<_, _>>()
346 .unwrap();
347 assert_eq!(decoded, items);
348 assert_eq!(prefixed_items(&[]).count(), 0);
349 }
350
351 #[test]
352 fn prefixed_items_reports_truncation_and_stops() {
353 let mut iterator = prefixed_items(&[4, 0xAA, 0xBB]);
355 assert_eq!(iterator.next(), Some(Err(ItemError::Truncated)));
356 assert_eq!(iterator.next(), None);
357
358 let mut iterator = prefixed_items(&[0x80, 0x80, 0x80, 0x80]);
360 assert_eq!(iterator.next(), Some(Err(ItemError::BadPrefix)));
361 assert_eq!(iterator.next(), None);
362 }
363
364 #[test]
365 fn encode_prefixed_item_reports_overflow() {
366 let mut small = [0u8; 3];
367 assert_eq!(
368 encode_prefixed_item(&[0; 8], &mut small),
369 Err(ItemError::BufferTooSmall)
370 );
371 }
372}