1use core::fmt;
2
3use alloc::vec::Vec;
4use sha2::Digest;
5
6use crate::buffer::StaticBuffer;
7use crate::crypt::fernet::{FERNET_MAX_PADDING_SIZE, FERNET_OVERHEAD_SIZE};
8use crate::error::RnsError;
9use crate::hash::AddressHash;
10use crate::hash::Hash;
11use crate::hash::ADDRESS_HASH_SIZE;
12
13pub const PACKET_MDU: usize = 464usize;
16pub const LXMF_MAX_PAYLOAD: usize = PACKET_MDU - FERNET_OVERHEAD_SIZE - FERNET_MAX_PADDING_SIZE;
17pub const PACKET_IFAC_MAX_LENGTH: usize = 64usize;
18
19#[derive(Debug, PartialEq, Eq, Copy, Clone)]
20pub enum IfacFlag {
21 Open = 0b0,
22 Authenticated = 0b1,
23}
24
25impl From<u8> for IfacFlag {
26 fn from(value: u8) -> Self {
27 match value {
28 0 => IfacFlag::Open,
29 1 => IfacFlag::Authenticated,
30 _ => IfacFlag::Open,
31 }
32 }
33}
34
35#[derive(Debug, PartialEq, Eq, Copy, Clone)]
36pub enum HeaderType {
37 Type1 = 0b0,
38 Type2 = 0b1,
39}
40
41impl From<u8> for HeaderType {
42 fn from(value: u8) -> Self {
43 match value & 0b1 {
44 0 => HeaderType::Type1,
45 1 => HeaderType::Type2,
46 _ => HeaderType::Type1,
47 }
48 }
49}
50
51#[derive(Debug, PartialEq, Eq, Copy, Clone)]
52pub enum PropagationType {
53 Broadcast = 0b0,
54 Transport = 0b1,
55}
56
57impl From<u8> for PropagationType {
58 fn from(value: u8) -> Self {
59 match value & 0b1 {
60 0b0 => PropagationType::Broadcast,
61 0b1 => PropagationType::Transport,
62 _ => PropagationType::Broadcast,
63 }
64 }
65}
66
67#[derive(Debug, PartialEq, Eq, Copy, Clone)]
68pub enum ContextFlag {
69 Unset = 0b0,
70 Set = 0b1,
71}
72
73impl From<u8> for ContextFlag {
74 fn from(value: u8) -> Self {
75 match value & 0b1 {
76 0b0 => ContextFlag::Unset,
77 0b1 => ContextFlag::Set,
78 _ => ContextFlag::Unset,
79 }
80 }
81}
82
83#[derive(Debug, PartialEq, Eq, Copy, Clone)]
84pub enum DestinationType {
85 Single = 0b00,
86 Group = 0b01,
87 Plain = 0b10,
88 Link = 0b11,
89}
90
91impl From<u8> for DestinationType {
92 fn from(value: u8) -> Self {
93 match value & 0b11 {
94 0b00 => DestinationType::Single,
95 0b01 => DestinationType::Group,
96 0b10 => DestinationType::Plain,
97 0b11 => DestinationType::Link,
98 _ => DestinationType::Single,
99 }
100 }
101}
102
103#[derive(Debug, PartialEq, Eq, Copy, Clone)]
104pub enum PacketType {
105 Data = 0b00,
106 Announce = 0b01,
107 LinkRequest = 0b10,
108 Proof = 0b11,
109}
110
111impl From<u8> for PacketType {
112 fn from(value: u8) -> Self {
113 match value & 0b11 {
114 0b00 => PacketType::Data,
115 0b01 => PacketType::Announce,
116 0b10 => PacketType::LinkRequest,
117 0b11 => PacketType::Proof,
118 _ => PacketType::Data,
119 }
120 }
121}
122
123#[derive(Debug, PartialEq, Eq, Copy, Clone)]
124pub enum PacketContext {
125 None = 0x00, Resource = 0x01, ResourceAdvrtisement = 0x02, ResourceRequest = 0x03, ResourceHashUpdate = 0x04, ResourceProof = 0x05, ResourceInitiatorCancel = 0x06, ResourceReceiverCancel = 0x07, CacheRequest = 0x08, Request = 0x09, Response = 0x0A, PathResponse = 0x0B, Command = 0x0C, CommandStatus = 0x0D, Channel = 0x0E, KeepAlive = 0xFA, LinkIdentify = 0xFB, LinkClose = 0xFC, LinkProof = 0xFD, LinkRTT = 0xFE, LinkRequestProof = 0xFF, }
147
148impl From<u8> for PacketContext {
149 fn from(value: u8) -> Self {
150 match value {
151 0x01 => PacketContext::Resource,
152 0x02 => PacketContext::ResourceAdvrtisement,
153 0x03 => PacketContext::ResourceRequest,
154 0x04 => PacketContext::ResourceHashUpdate,
155 0x05 => PacketContext::ResourceProof,
156 0x06 => PacketContext::ResourceInitiatorCancel,
157 0x07 => PacketContext::ResourceReceiverCancel,
158 0x08 => PacketContext::CacheRequest,
159 0x09 => PacketContext::Request,
160 0x0A => PacketContext::Response,
161 0x0B => PacketContext::PathResponse,
162 0x0C => PacketContext::Command,
163 0x0D => PacketContext::CommandStatus,
164 0x0E => PacketContext::Channel,
165 0xFA => PacketContext::KeepAlive,
166 0xFB => PacketContext::LinkIdentify,
167 0xFC => PacketContext::LinkClose,
168 0xFD => PacketContext::LinkProof,
169 0xFE => PacketContext::LinkRTT,
170 0xFF => PacketContext::LinkRequestProof,
171 _ => PacketContext::None,
172 }
173 }
174}
175
176#[derive(Debug, PartialEq, Eq, Copy, Clone)]
177pub struct Header {
178 pub ifac_flag: IfacFlag,
179 pub header_type: HeaderType,
180 pub context_flag: ContextFlag,
181 pub propagation_type: PropagationType,
182 pub destination_type: DestinationType,
183 pub packet_type: PacketType,
184 pub hops: u8,
185}
186
187impl Default for Header {
188 fn default() -> Self {
189 Self {
190 ifac_flag: IfacFlag::Open,
191 header_type: HeaderType::Type1,
192 context_flag: ContextFlag::Unset,
193 propagation_type: PropagationType::Broadcast,
194 destination_type: DestinationType::Single,
195 packet_type: PacketType::Data,
196 hops: 0,
197 }
198 }
199}
200
201impl Header {
202 pub fn to_meta(&self) -> u8 {
203 (self.ifac_flag as u8) << 7
204 | (self.header_type as u8) << 6
205 | (self.context_flag as u8) << 5
206 | (self.propagation_type as u8) << 4
207 | (self.destination_type as u8) << 2
208 | (self.packet_type as u8)
209 }
210
211 pub fn from_meta(meta: u8) -> Self {
212 Self {
213 ifac_flag: IfacFlag::from(meta >> 7),
214 header_type: HeaderType::from(meta >> 6),
215 context_flag: ContextFlag::from(meta >> 5),
216 propagation_type: PropagationType::from(meta >> 4),
217 destination_type: DestinationType::from(meta >> 2),
218 packet_type: PacketType::from(meta),
219 hops: 0,
220 }
221 }
222}
223
224impl fmt::Display for Header {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(
227 f,
228 "{:b}{:b}{:b}{:b}{:0>2b}{:0>2b}.{}",
229 self.ifac_flag as u8,
230 self.header_type as u8,
231 self.context_flag as u8,
232 self.propagation_type as u8,
233 self.destination_type as u8,
234 self.packet_type as u8,
235 self.hops,
236 )
237 }
238}
239
240pub type PacketDataBuffer = StaticBuffer<PACKET_MDU>;
241
242#[derive(Debug, PartialEq, Eq, Copy, Clone)]
243pub struct PacketIfac {
244 pub access_code: [u8; PACKET_IFAC_MAX_LENGTH],
245 pub length: usize,
246}
247
248impl PacketIfac {
249 pub fn new_from_slice(slice: &[u8]) -> Self {
250 let mut access_code = [0u8; PACKET_IFAC_MAX_LENGTH];
251 access_code[..slice.len()].copy_from_slice(slice);
252 Self { access_code, length: slice.len() }
253 }
254
255 pub fn as_slice(&self) -> &[u8] {
256 &self.access_code[..self.length]
257 }
258}
259
260#[derive(Debug, PartialEq, Eq, Copy, Clone)]
261pub struct Packet {
262 pub header: Header,
263 pub ifac: Option<PacketIfac>,
264 pub destination: AddressHash,
265 pub transport: Option<AddressHash>,
266 pub context: PacketContext,
267 pub data: PacketDataBuffer,
268}
269
270impl Packet {
271 pub const LXMF_MAX_PAYLOAD: usize = LXMF_MAX_PAYLOAD;
272
273 pub fn from_bytes(bytes: &[u8]) -> Result<Self, RnsError> {
274 let min_len = 2 + ADDRESS_HASH_SIZE + 1;
275 if bytes.len() < min_len {
276 return Err(RnsError::InvalidArgument);
277 }
278
279 let flags = bytes[0];
280 let hops = bytes[1];
281
282 let mut header = Header::from_meta(flags);
283 header.hops = hops;
284
285 let mut idx = 2;
286
287 let transport = if header.header_type == HeaderType::Type2 {
288 if bytes.len() < idx + ADDRESS_HASH_SIZE {
289 return Err(RnsError::InvalidArgument);
290 }
291 let mut raw = [0u8; ADDRESS_HASH_SIZE];
292 raw.copy_from_slice(&bytes[idx..idx + ADDRESS_HASH_SIZE]);
293 idx += ADDRESS_HASH_SIZE;
294 Some(AddressHash::new(raw))
295 } else {
296 None
297 };
298
299 if bytes.len() < idx + ADDRESS_HASH_SIZE + 1 {
300 return Err(RnsError::InvalidArgument);
301 }
302
303 let mut dest_raw = [0u8; ADDRESS_HASH_SIZE];
304 dest_raw.copy_from_slice(&bytes[idx..idx + ADDRESS_HASH_SIZE]);
305 idx += ADDRESS_HASH_SIZE;
306 let destination = AddressHash::new(dest_raw);
307
308 let context = PacketContext::from(bytes[idx]);
309 idx += 1;
310
311 let data = PacketDataBuffer::new_from_slice(&bytes[idx..]);
312
313 Ok(Self { header, ifac: None, destination, transport, context, data })
314 }
315
316 pub fn to_bytes(&self) -> Result<Vec<u8>, RnsError> {
317 let mut out = Vec::with_capacity(2 + ADDRESS_HASH_SIZE + 1 + self.data.len());
318
319 out.push(self.header.to_meta());
320 out.push(self.header.hops);
321
322 if self.header.header_type == HeaderType::Type2 {
323 let transport = self.transport.ok_or(RnsError::InvalidArgument)?;
324 out.extend_from_slice(transport.as_slice());
325 }
326
327 out.extend_from_slice(self.destination.as_slice());
328 out.push(self.context as u8);
329 out.extend_from_slice(self.data.as_slice());
330
331 Ok(out)
332 }
333
334 pub fn hash(&self) -> Hash {
335 Hash::new(
336 Hash::generator()
337 .chain_update([self.header.to_meta() & 0b00001111])
338 .chain_update(self.destination.as_slice())
339 .chain_update([self.context as u8])
340 .chain_update(self.data.as_slice())
341 .finalize()
342 .into(),
343 )
344 }
345
346 pub fn fragment_for_lxmf(data: &[u8]) -> Result<Vec<Packet>, RnsError> {
347 let mut out = Vec::new();
348 for chunk in data.chunks(Self::LXMF_MAX_PAYLOAD) {
349 let packet = Packet { data: StaticBuffer::new_from_slice(chunk), ..Default::default() };
350 out.push(packet);
351 }
352 Ok(out)
353 }
354}
355
356impl Default for Packet {
357 fn default() -> Self {
358 Self {
359 header: Default::default(),
360 destination: AddressHash::new_empty(),
361 data: Default::default(),
362 ifac: None,
363 transport: None,
364 context: crate::packet::PacketContext::None,
365 }
366 }
367}
368
369impl fmt::Display for Packet {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 write!(f, "[{}", self.header)?;
372
373 if let Some(transport) = self.transport {
374 write!(f, " {}", transport)?;
375 }
376
377 write!(f, " {}", self.destination)?;
378
379 write!(f, " 0x[{}]]", self.data.len())?;
380
381 Ok(())
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::{
388 ContextFlag, DestinationType, Header, HeaderType, IfacFlag, PacketType, PropagationType,
389 };
390
391 #[test]
392 fn header_meta_roundtrip_preserves_context_and_transport_bits() {
393 let header = Header {
394 ifac_flag: IfacFlag::Open,
395 header_type: HeaderType::Type1,
396 context_flag: ContextFlag::Set,
397 propagation_type: PropagationType::Transport,
398 destination_type: DestinationType::Single,
399 packet_type: PacketType::Announce,
400 hops: 0,
401 };
402
403 let meta = header.to_meta();
404 assert_eq!(meta & 0b0010_0000, 0b0010_0000);
405 assert_eq!(meta & 0b0001_0000, 0b0001_0000);
406
407 let decoded = Header::from_meta(meta);
408 assert_eq!(decoded.context_flag, ContextFlag::Set);
409 assert_eq!(decoded.propagation_type, PropagationType::Transport);
410 }
411}