#[cfg(feature = "std")] use crate::std_identity::Keystore; #[cfg(not(feature = "std"))] use crate::no_std_identity::Keystore; use crate::{ anon_req::ClearAnonRequest, packet_content::PacketContent, request::ClearRequest, response::ClearResponse, text::ClearText, }; use bytes::{Buf, Bytes}; use tinyvec::ArrayVec; #[derive(PartialEq, Clone, core::fmt::Debug)] pub struct Packet { pub route_type: RouteType, pub version: PayloadVersion, pub path: ArrayVec<[u16; 64]>, pub transport: [u16; 2], pub raw_content: Bytes, pub content: PacketContent, pub incomplete: bool, } impl core::str::FromStr for Packet { type Err = hex::FromHexError; fn from_str(hex_str: &str) -> Result { let hex = hex::decode(hex_str)?; Ok(Packet::from(Bytes::copy_from_slice(&hex))) } } impl From for Packet { fn from(bytes_in: Bytes) -> Self { let mut bytes = bytes_in; // This is the packet we'll build as we begin to parse let mut packet = Packet::default(); if bytes.len() < 2 { return packet; } let header = bytes.get_u8(); // Parse the header byte packet.route_type = RouteType::from(header); packet.version = PayloadVersion::from(header); // Get the transport if it's provided packet.transport = match packet.route_type { RouteType::TransportFlood | RouteType::TransportDirect => { // The packet isn't long enough to contain the transport if bytes.len() < 4 { return packet; } let t1 = bytes.get_u16_le(); let t2 = bytes.get_u16_le(); [t1, t2] } _ => [0, 0], }; // Get the route if bytes.is_empty() { return packet; } let path_length = bytes.get_u8() as usize; packet.path = match packet.version { PayloadVersion::VersionOne => { // The packet isn't long enough for the indicated route if bytes.len() < path_length { return packet; } let mut route = ArrayVec::new(); for _ in 0..path_length { let path_element = bytes.get_u8() as u16; route.push(path_element); } route } PayloadVersion::VersionTwo => { // The packet isn't long enough for the indicated route if bytes.len() < path_length * 2 { return packet; } let mut route = ArrayVec::new(); for i in 0..path_length { let path_element = bytes.get_u16(); route[i] = path_element; } route } _ => { return packet; } }; // Get the rest of the payload and pass the parsing to the other structs packet.raw_content = bytes.clone(); packet.content = PacketContent::new(header, bytes); // Unfortunately, the trace packet is a bit weird. // So, if this is a trace packet, move things back into the right place. if let PacketContent::Trace(trace) = packet.content { let mut trace = trace; let path_snr = packet.path; for (index, element) in trace.temp_path.iter().enumerate() { packet.path[index] = *element as u16; } for u in path_snr.iter() { let i = *u as i8; let f = i as f32; trace.path_snr.push(f / 4.0); } // DON'T delete the temp path, so Trace has access to the path // when it prints the SNR results in its Display function. packet.content = PacketContent::Trace(trace); } // Mark the packet as complete and valid packet.incomplete = false; packet } } impl Default for Packet { fn default() -> Self { Packet { route_type: RouteType::Invalid, version: PayloadVersion::Invalid, path: ArrayVec::new(), transport: [0, 0], raw_content: Bytes::new(), content: PacketContent::Invalid, incomplete: true, } } } impl core::fmt::Display for Packet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::fmt::Display::fmt(&self.route_type, f)?; f.write_str(" | ")?; core::fmt::Display::fmt(&self.version, f)?; if self.route_type == RouteType::TransportDirect || self.route_type == RouteType::TransportFlood { f.write_fmt(format_args!( " | {:4x?}, {:4x?} | ", self.transport[0], self.transport[1] ))? } else { f.write_fmt(format_args!(" | |"))? } let len = self.path.len(); match self.path.len() { 0 => f.write_fmt(format_args!(" [] | ")), 1 => f.write_fmt(format_args!(" [{:02x?}] | ", self.path[0])), 2 => f.write_fmt(format_args!( " [{:02x?}, {:02x?}] | ", self.path[0], self.path[1] )), 3 => f.write_fmt(format_args!( " [{:02x?}, {:02x?}, {:02x?}] | ", self.path[0], self.path[1], self.path[2] )), _ => f.write_fmt(format_args!( " [{:02x?}, {:02x?}, ... {:02x?}, {:02x?}] | ", self.path[0], self.path[1], self.path[len - 2], self.path[len - 1] )), }?; if self.incomplete { f.write_str("x | ") } else { f.write_str(" | ") }?; core::fmt::Display::fmt(&self.content, f) } } impl Packet { #[cfg(feature = "std")] pub fn try_decrypt(&mut self, keystore: &Keystore) -> bool { match self.content { // Encrypted packet types PacketContent::Path(ref mut path) => path.cipher.try_decrypt(keystore), PacketContent::Request(ref mut request) => { let result = request.cipher.try_decrypt(keystore); if let Some(cleartext) = &request.cipher.cleartext { let cleartext = ClearRequest::from(cleartext.clone()); request.cleartext = Some(cleartext); result } else { false } } PacketContent::Response(ref mut response) => { response.cipher.try_decrypt(keystore); if let Some(cleartext) = &response.cipher.cleartext { response.cleartext = Some(ClearResponse::from(cleartext.to_owned())); true } else { false } } PacketContent::Text(ref mut text) => { text.cipher.try_decrypt(keystore); if let Some(cleartext) = &text.cipher.cleartext { text.cleartext = Some(ClearText::from(cleartext.to_owned())); true } else { false } } PacketContent::AnonReq(ref mut anon_req) => { let decrypt_result = keystore.decrypt_anon( anon_req.dest, &anon_req.public_key, anon_req.mac, &anon_req.ciphertext, ); if let Some(cleartext) = decrypt_result { anon_req.request = Some(ClearAnonRequest::from(cleartext.0)); true } else { false } } PacketContent::GroupText(ref mut group_text) => group_text.try_decrypt(keystore), // None of the other packets implement any encryption _ => false, } } } #[derive(PartialEq, Debug, Clone)] pub enum RouteType { TransportFlood, Flood, Direct, TransportDirect, Invalid, } impl core::fmt::Display for RouteType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { RouteType::TransportFlood => f.write_str("T-Flood "), RouteType::Flood => f.write_str(" Flood "), RouteType::Direct => f.write_str(" Direct"), RouteType::TransportDirect => f.write_str("T-Direct"), RouteType::Invalid => f.write_str("INVALID "), } } } impl From for RouteType { fn from(value: u8) -> Self { match value & 0x03 { 0x00 => RouteType::TransportFlood, 0x01 => RouteType::Flood, 0x02 => RouteType::Direct, 0x03 => RouteType::TransportDirect, _ => RouteType::Invalid, } } } #[derive(PartialEq, Debug, Clone)] pub enum PayloadVersion { VersionOne, VersionTwo, VersionThree, VersionFour, Invalid, } impl From for PayloadVersion { fn from(value: u8) -> Self { let value = (value & 0xC0) >> 6; assert!(value < 4, "Programming error in masking and bit-shifting"); match value { 0x00 => PayloadVersion::VersionOne, 0x01 => PayloadVersion::VersionTwo, 0x02 => PayloadVersion::VersionThree, 0x03 => PayloadVersion::VersionFour, _ => PayloadVersion::Invalid, } } } impl core::fmt::Display for PayloadVersion { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { PayloadVersion::VersionOne => f.write_str("v1"), PayloadVersion::VersionTwo => f.write_str("v2"), PayloadVersion::VersionThree => f.write_str("v3"), PayloadVersion::VersionFour => f.write_str("v4"), PayloadVersion::Invalid => f.write_str("xx"), } } } #[cfg(test)] mod tests { use core::str::FromStr; use tinyvec::array_vec; use super::*; use crate::packet_content::{PacketContent, Raw}; #[test] fn header_route_type() { // The route type is the lowest-order two bits assert_eq!(RouteType::TransportFlood, RouteType::from(0x00)); assert_eq!(RouteType::TransportFlood, RouteType::from(0xFC)); assert_eq!(RouteType::Flood, RouteType::from(0x01)); assert_eq!(RouteType::Flood, RouteType::from(0xFD)); assert_eq!(RouteType::Direct, RouteType::from(0x02)); assert_eq!(RouteType::Direct, RouteType::from(0xFE)); assert_eq!(RouteType::TransportDirect, RouteType::from(0x03)); assert_eq!(RouteType::TransportDirect, RouteType::from(0xFF)); assert_eq!(format!("{}", RouteType::TransportFlood), "T-Flood "); assert_eq!(format!("{}", RouteType::Flood), " Flood "); assert_eq!(format!("{}", RouteType::Direct), " Direct"); assert_eq!(format!("{}", RouteType::TransportDirect), "T-Direct"); assert_eq!(format!("{}", RouteType::Invalid), "INVALID "); } #[test] fn header_version() { assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x00)); assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x3F)); assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x40)); assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x7F)); assert_eq!(PayloadVersion::VersionThree, PayloadVersion::from(0x80)); assert_eq!(PayloadVersion::VersionThree, PayloadVersion::from(0xBF)); assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xC0)); assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xFF)); assert_eq!(format!("{}", PayloadVersion::VersionOne), "v1"); assert_eq!(format!("{}", PayloadVersion::VersionTwo), "v2"); assert_eq!(format!("{}", PayloadVersion::VersionThree), "v3"); assert_eq!(format!("{}", PayloadVersion::VersionFour), "v4"); assert_eq!(format!("{}", PayloadVersion::Invalid), "xx"); } #[test] fn packet() { // Check the hex decode errors assert_eq!( Err(hex::FromHexError::InvalidHexCharacter { c: 's', index: 0 }), Packet::from_str("s0") ); assert_eq!(Err(hex::FromHexError::OddLength), Packet::from_str("0")); // Check errors related to packet length issues assert_eq!(Packet::default(), Packet::from_str("").unwrap()); let rhs_packet = Packet::from_str("01").unwrap(); assert_eq!(Packet::default(), rhs_packet); // Packet not long enough to contain the provided path let mut lhs_packet = Packet::default(); lhs_packet.route_type = RouteType::Flood; lhs_packet.version = PayloadVersion::VersionOne; let rhs_packet = Packet::from_str("0101").unwrap(); assert_eq!(lhs_packet, rhs_packet); // Packet not long enough for transport let mut lhs_packet = Packet::default(); lhs_packet.route_type = RouteType::TransportDirect; lhs_packet.version = PayloadVersion::VersionOne; let rhs_packet = Packet::from_str("0301").unwrap(); assert_eq!(lhs_packet, rhs_packet); // Packet not long enough for version 2 path let mut lhs_packet = Packet::default(); lhs_packet.route_type = RouteType::Direct; lhs_packet.version = PayloadVersion::VersionTwo; let rhs_packet = Packet::from_str("420102").unwrap(); assert_eq!(lhs_packet, rhs_packet); // Ensure packet remainder is captured. This // will test version 1 plus transport let lhs_packet = Packet { route_type: RouteType::TransportDirect, version: PayloadVersion::VersionOne, path: array_vec!([u16; 64] => 0x06, 0x07, 0x08, 0x09, 0x0A), transport: [0x0102, 0x0304], raw_content: Bytes::copy_from_slice(&[ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, ]), content: PacketContent::Raw(Raw { bytes: Bytes::copy_from_slice(&[ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, ]), }), incomplete: false, }; let rhs_packet: Packet = Packet::from_str("3F0201040305060708090A10111213141516171819").unwrap(); let compare_string = "T-Direct | v1 | 102, 304 | [06, 07, ... 09, 0a] | | RAW | Raw { bytes: b\"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\" }"; assert_eq!(format!("{}", rhs_packet), compare_string); assert_eq!(lhs_packet, rhs_packet); } #[test] fn packet_display() { let mut packet = Packet { route_type: RouteType::Direct, version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 1], raw_content: Bytes::new(), content: PacketContent::Invalid, incomplete: false, }; assert_eq!( format!("{}", packet), " Direct | v1 | | [] | | INVALID | INVALID" ); packet.path.push(0x01); assert_eq!( format!("{}", packet), " Direct | v1 | | [01] | | INVALID | INVALID" ); packet.path.push(0x02); assert_eq!( format!("{}", packet), " Direct | v1 | | [01, 02] | | INVALID | INVALID" ); packet.path.push(0xff); assert_eq!( format!("{}", packet), " Direct | v1 | | [01, 02, ff] | | INVALID | INVALID" ); packet.incomplete = true; assert_eq!( format!("{}", packet), " Direct | v1 | | [01, 02, ff] | x | INVALID | INVALID" ); } }