diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/packet.rs | 342 |
1 files changed, 318 insertions, 24 deletions
diff --git a/src/packet.rs b/src/packet.rs index dd43130..269835a 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, Local, Utc}; use hex::decode; use tokio_util::bytes::{Buf, Bytes}; use structdiff::{Difference, StructDiff}; -use crate::{crypto::SharedSecret, crypto::PublicKey}; +use crate::crypto::{PrivateKey, PublicKey, SharedSecret}; #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] @@ -125,39 +125,99 @@ impl From<u8> for NodeType { } } +#[derive(PartialEq, Debug, Clone, Difference)] +#[difference(expose)] +struct PeerToPeerCipher { + destination: u8, + source: u8, + mac: u16, + ciphertext: Bytes +} + +impl From<Bytes> for PeerToPeerCipher { + fn from(value: Bytes) -> Self { + let mut bytes = value; + + let mut response = PeerToPeerCipher { + destination: 0x00, + source: 0x00, + mac: 0x0000, + ciphertext: Bytes::new() + }; + + // Just check for the whole fixed-size part at once + if bytes.len() < 4 { return response } + response.destination = bytes.get_u8(); + response.source = bytes.get_u8(); + response.mac = bytes.get_u16(); + + response.ciphertext = bytes; + + response + } +} + +impl PeerToPeerCipher { + pub fn attempt_decrypt(&mut self, secret: SharedSecret) -> Option<(Bytes, PublicKey, PrivateKey)> { + None + } +} + #[derive(PartialEq, Debug, Clone)] -struct Request {} +struct Request { + cipher: PeerToPeerCipher, +} impl From<Bytes> for Request { fn from(value: Bytes) -> Self { - todo!() + let request = Request { + cipher: PeerToPeerCipher::from(value) + }; + + request } } -#[derive(PartialEq, Debug, Clone)] -struct Response {} +#[derive(PartialEq, Debug, Clone, Difference)] +#[difference(expose)] +struct Response { + cipher: PeerToPeerCipher, +} impl From<Bytes> for Response { fn from(value: Bytes) -> Self { - todo!() + let response = Response { + cipher: PeerToPeerCipher::from(value) + }; + + response } } #[derive(PartialEq, Debug, Clone)] -struct Text {} +struct Text { + cipher: PeerToPeerCipher, +} impl From<Bytes> for Text { fn from(value: Bytes) -> Self { - todo!() + let mut text = Text { + cipher: PeerToPeerCipher::from(value) + }; + + text } } #[derive(PartialEq, Debug, Clone)] -struct Ack {} +struct Ack { + checksum: u32 +} impl From<Bytes> for Ack { fn from(value: Bytes) -> Self { - todo!() + let mut bytes = value; + Ack { checksum: bytes.get_u32() } } } @@ -251,11 +311,36 @@ impl From<Bytes> for Advert { } #[derive(PartialEq, Debug, Clone)] -struct GroupText {} +struct GroupText { + hash: u8, + mac: u16, + ciphertext: Bytes, + + cleartext: Option<String>, + + incomplete: bool +} impl From<Bytes> for GroupText { fn from(value: Bytes) -> Self { - todo!() + let mut bytes = value; + + let mut group_text = GroupText { + hash: 0x00, + mac: 0x0000, + ciphertext: Bytes::new(), + cleartext: None, + incomplete: true, + }; + + // Just check for the whole fixed-size part at once + if bytes.len() < 3 { return group_text } + group_text.hash = bytes.get_u8(); + group_text.mac = bytes.get_u16(); + + group_text.ciphertext = bytes; + group_text.incomplete = false; + group_text } } @@ -278,7 +363,9 @@ impl From<Bytes> for AnonReq { } #[derive(PartialEq, Debug, Clone)] -struct Path {} +struct Path { + cipher: PeerToPeerCipher, +} impl From<Bytes> for Path { fn from(value: Bytes) -> Self { @@ -287,11 +374,37 @@ impl From<Bytes> for Path { } #[derive(PartialEq, Debug, Clone)] -struct Trace {} +struct Trace { + tag: u32, + auth: u32, + flags: u8, + path_snr: Vec<f32>, + invalid: bool, + + temp_path: Vec<u8> +} impl From<Bytes> for Trace { fn from(value: Bytes) -> Self { - todo!() + let mut bytes = value; + + let mut trace = Trace { + tag: 0, + auth: 0, + flags: 0, + path_snr: vec![], + invalid: true, + temp_path: vec![] + }; + + if bytes.len() < 10 { return trace; } + + trace.tag = bytes.get_u32(); + trace.auth = bytes.get_u32(); + trace.flags = bytes.get_u8(); + trace.temp_path = bytes.iter().map(|s| *s).collect(); + trace.invalid = false; + trace } } @@ -388,6 +501,24 @@ impl From<Bytes> for Packet { 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. + match packet.content { + PacketContent::Trace(trace) => { + let mut trace = trace; + let path_snr = packet.path; + packet.path = trace.temp_path.iter().map(|i| *i as u16).collect(); + trace.path_snr = path_snr.iter().map( |u| { + let i = *u as i8; + let f = i as f32; + f / 4.0 + }).collect(); + trace.temp_path = vec![]; + packet.content = PacketContent::Trace(trace); + }, + _ => {} + } + // Mark the packet as complete and valid packet.incomplete = false; @@ -395,13 +526,6 @@ impl From<Bytes> for Packet { } } -impl Packet { - pub fn attempt_decrypt(&mut self, secret: SharedSecret) -> bool { - false - } - -} - impl Default for Packet { fn default() -> Self { Packet { @@ -463,7 +587,7 @@ mod tests { } fn print_compare(lhs: Packet, rhs: Packet) -> String { - let mut output_string = format!("Differences between packets: \n"); + let mut output_string = format!("Left hand side: \n{:#?}\nRight hand side: \n{:#?}\nDifferences: \n", lhs, rhs); for diff in lhs.diff(&rhs) { output_string.push_str(&format!("{:#?}", diff)); @@ -519,7 +643,7 @@ mod tests { } #[test] - fn advert() { + fn advert_with_lat_lon() { // Real sample packet from the air let sample = "110c015f7e9b60661da0f2671512460728508c17ef336412a223144d3a623215162682045c44fef7241af0161923d3af0769d2d976b687a506dd5325ef526bf3eb52ae687277fcbde9969a5b0087e0eb0f7c1760a50c6a88bec13cc30a2a9b681d713166515e3bbc2bc27f20c0e4d7b67e08910c29dc02c468b8f8484f574c"; @@ -551,7 +675,177 @@ mod tests { } #[test] + fn advert() { + // Another on-air sample + let sample = "12007890b8573a6ba4a05b173d6ccfdfa73ac8ec4a12bf3c745ace636e1d191e132a4eb407695601f50907999735f3699a3c73ace2d8acc385ed209606abef6b914a346fbb88648c540ae794586b4d54eb08b473c8571a00c6b8d3dbcc77699afa169811fa098168707578373335"; + + let mut signature_slice = [0 as u8; 64]; + hex::decode_to_slice("5601f50907999735f3699a3c73ace2d8acc385ed209606abef6b914a346fbb88648c540ae794586b4d54eb08b473c8571a00c6b8d3dbcc77699afa169811fa09", &mut signature_slice).unwrap(); + + let lhs_packet = Packet { + route_type: RouteType::Direct, + version: PayloadVersion::VersionOne, + path: vec![], + transport: [0x00, 0x00], + raw_content: Bytes::copy_from_slice(&decode("7890b8573a6ba4a05b173d6ccfdfa73ac8ec4a12bf3c745ace636e1d191e132a4eb407695601f50907999735f3699a3c73ace2d8acc385ed209606abef6b914a346fbb88648c540ae794586b4d54eb08b473c8571a00c6b8d3dbcc77699afa169811fa098168707578373335").unwrap()), + content: PacketContent::Advert(Advert { + public_key: PublicKey::from_str("7890b8573a6ba4a05b173d6ccfdfa73ac8ec4a12bf3c745ace636e1d191e132a").unwrap(), + timestamp: DateTime::from_timestamp(1762112590, 0).unwrap(), + signature: signature_slice, + node_type: NodeType::Chat, + latitude: None, + longitude: None, + feature1: None, + feature2: None, + name: "hpux735".to_owned(), + }), + incomplete: false, + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] + fn response() { + let sample = "06003412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92"; + + let lhs_packet = Packet { + route_type: RouteType::Direct, + version: PayloadVersion::VersionOne, + path: vec![], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()), + content: PacketContent::Response(Response { + cipher: PeerToPeerCipher { + destination: 0x34, + source: 0x12, + mac: 0xb900, + ciphertext: Bytes::copy_from_slice(&decode("0cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()) + }}), + incomplete: false, + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] fn request() { + let sample = "020012341d87ccaac89563cbb39d2333b725e407a1a6"; + + let lhs_packet = Packet { + route_type: RouteType::Direct, + version: PayloadVersion::VersionOne, + path: vec![], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("12341d87ccaac89563cbb39d2333b725e407a1a6").unwrap()), + content: PacketContent::Request(Request { + cipher: PeerToPeerCipher { + destination: 0x12, + source: 0x34, + mac: 0x1d87, + ciphertext: Bytes::copy_from_slice(&decode("ccaac89563cbb39d2333b725e407a1a6").unwrap()) + }}), + incomplete: false, + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] + fn text() { + let sample = "0a001234e91c8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9"; + + let lhs_packet = Packet { + route_type: RouteType::Direct, + version: PayloadVersion::VersionOne, + path: vec![], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("1234e91c8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9").unwrap()), + content: PacketContent::Text(Text { + cipher: PeerToPeerCipher { + destination: 0x12, + source: 0x34, + mac: 0xe91c, + ciphertext: Bytes::copy_from_slice(&decode("8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9").unwrap()) + }}), + incomplete: false, + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] + fn ack() { + let sample = "0D0E78B5561B03CD70DA326066B8A0CF24F3214D"; + + let lhs_packet = Packet { + route_type: RouteType::Flood, + version: PayloadVersion::VersionOne, + path: vec![0x78, 0xB5, 0x56, 0x1B, 0x03, 0xCD, 0x70, 0xDA, 0x32, 0x60, 0x66, 0xB8, 0xA0, 0xCF], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("24F3214D").unwrap()), + content: PacketContent::Ack(Ack { + checksum: 0x24F3214D + }), + incomplete: false + }; + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); } + + #[test] + fn trace() { + let sample = "26032CE0F149EB5B240000000000ACA079"; + + let lhs_packet = Packet { + route_type: RouteType::Direct, + version: PayloadVersion::VersionOne, + path: vec![0xAC, 0xA0, 0x79], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("49EB5B240000000000ACA079").unwrap()), + content: PacketContent::Trace(Trace { + tag: 0x49EB5B24, + auth: 0x00000000, + flags: 0x00, + path_snr: vec![11.0, -8.0, -3.75], + invalid: false, + temp_path: vec![] + } + ), + incomplete: false + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] + fn group_text() { + let sample = "15107B2CF1A31C03E8AACD7E6066B8A0ACB71176B87DDF8FE67B33E7A63036E015311EE39232F094FAAD13C4442947947DD098886BC677DE2B6F456149D0A1B05C72F0EECC20DC07EDF163D9D63EBC9BC29A7C79AF"; + + let lhs_packet = Packet { + route_type: RouteType::Flood, + version: PayloadVersion::VersionOne, + path: vec![0x7B, 0x2C, 0xF1, 0xA3, 0x1C, 0x03, 0xE8, 0xAA, 0xCD, 0x7E, 0x60, 0x66, 0xB8, 0xA0, 0xAC, 0xB7], + transport: [0, 0], + raw_content: Bytes::copy_from_slice(&decode("1176B87DDF8FE67B33E7A63036E015311EE39232F094FAAD13C4442947947DD098886BC677DE2B6F456149D0A1B05C72F0EECC20DC07EDF163D9D63EBC9BC29A7C79AF").unwrap()), + content: PacketContent::GroupText(GroupText { + hash: 0x11, + mac: 0x76B8, + ciphertext: Bytes::copy_from_slice(&decode("7DDF8FE67B33E7A63036E015311EE39232F094FAAD13C4442947947DD098886BC677DE2B6F456149D0A1B05C72F0EECC20DC07EDF163D9D63EBC9BC29A7C79AF").unwrap()), + cleartext: None, // "Just need some more high repeaters".to_owned() + incomplete: false + }), + incomplete: false + }; + + let rhs_packet = Packet::from_str(sample).unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + }
\ No newline at end of file |
