diff options
Diffstat (limited to 'src/text.rs')
| -rw-r--r-- | src/text.rs | 208 |
1 files changed, 127 insertions, 81 deletions
diff --git a/src/text.rs b/src/text.rs index df02822..5d89870 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,9 +1,11 @@ use bytes::{Buf, Bytes}; use chrono::{DateTime, Utc}; -use crate::string_helper::{MessageString, NameString, message_string_from_slice, name_string_from_slice}; #[cfg(feature = "std")] -use crate::{packet_content::PeerToPeerCipher}; +use crate::packet_content::PeerToPeerCipher; +use crate::string_helper::{ + MessageString, NameString, message_string_from_slice, name_string_from_slice, +}; #[cfg(not(feature = "std"))] use crate::no_std_identity::Keystore; @@ -21,32 +23,32 @@ impl From<Bytes> for Text { fn from(value: Bytes) -> Self { Text { cipher: PeerToPeerCipher::from(value), - cleartext: None + cleartext: None, } } } impl core::fmt::Display for Text { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", - self.cipher.source, - self.cipher.destination, - self.cipher.mac + f.write_fmt(format_args!( + "({:2x?}) -> ({:2x?}) MAC: {:4x?} ", + self.cipher.source, self.cipher.destination, self.cipher.mac ))?; if let Some(cleartext) = &self.cleartext { - f.write_fmt(format_args!("at: {}, {} attempts, from {}: {}", + f.write_fmt(format_args!( + "at: {}, {} attempts, from {}: {}", cleartext.timestamp, cleartext.attempts, cleartext.sender, - cleartext.message.replace("\n", "\\n"))) + cleartext.message.replace("\n", "\\n") + )) } else { f.write_str("ENCRYPTED") } } } - #[derive(PartialEq, Debug, Clone)] pub struct ClearText { pub timestamp: DateTime<Utc>, @@ -74,7 +76,9 @@ impl From<Bytes> for ClearText { }; // Just check for the whole fixed-size part at once - if bytes.len() < 5 { return clear_text } + if bytes.len() < 5 { + return clear_text; + } if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32_le() as i64, 0) { clear_text.timestamp = timestamp; } @@ -83,8 +87,7 @@ impl From<Bytes> for ClearText { clear_text.message_type = MessageType::from(flags); clear_text.attempts = flags & 0x03; - if clear_text.message_type == MessageType::SignedPlain && - bytes.len() > 4 { + if clear_text.message_type == MessageType::SignedPlain && bytes.len() > 4 { clear_text.sender_hash = bytes.get_u32(); } @@ -97,7 +100,7 @@ impl From<Bytes> for ClearText { let splits_iter = bytes.splitn(2, |c| *c == b':'); let mut splits: [Option<&[u8]>; 2] = [None; 2]; - + for (index, split) in splits_iter.enumerate() { splits[index] = Some(split); } @@ -106,13 +109,13 @@ impl From<Bytes> for ClearText { (Some(message), None) => { clear_text.sender = name_string_from_slice(b"Unknown"); clear_text.message = message_string_from_slice(message); - }, + } (Some(sender), Some(message)) => { clear_text.sender = name_string_from_slice(sender); clear_text.message = message_string_from_slice(message); - }, - + } + _ => {} } @@ -122,7 +125,7 @@ impl From<Bytes> for ClearText { #[derive(PartialEq, Debug, Clone)] pub struct GroupData { - pub(crate) payload: Bytes + pub(crate) payload: Bytes, } impl From<Bytes> for GroupData { @@ -145,7 +148,7 @@ pub struct GroupText { cleartext: Option<ClearText>, - incomplete: bool + incomplete: bool, } impl From<Bytes> for GroupText { @@ -161,7 +164,9 @@ impl From<Bytes> for GroupText { }; // Just check for the whole fixed-size part at once - if bytes.len() < 3 { return group_text } + if bytes.len() < 3 { + return group_text; + } group_text.hash = bytes.get_u8(); group_text.mac = bytes.get_u16(); @@ -174,11 +179,7 @@ impl From<Bytes> for GroupText { impl GroupText { #[cfg(feature = "std")] pub fn try_decrypt(&mut self, keysore: &Keystore) -> bool { - let decrypt_result = keysore.decrypt_and_id_group( - self.hash, - self.mac, - &self.ciphertext - ); + let decrypt_result = keysore.decrypt_and_id_group(self.hash, self.mac, &self.ciphertext); if let Some((cleartext, group)) = decrypt_result { let mut cleartext = ClearText::from(cleartext); @@ -194,17 +195,17 @@ impl GroupText { impl core::fmt::Display for GroupText { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if let Some(cleartext) = &self.cleartext { - let message = cleartext.message.replace("\n", "\\n"); - f.write_fmt(format_args!("({:2x?}) Mac: {:4x?} Group {}: {}", - self.hash, - self.mac, - cleartext.crypto_recipient, - message) - ) + f.write_fmt(format_args!( + "({:2x?}) Mac: {:4x?} Group {}: {}", + self.hash, self.mac, cleartext.crypto_recipient, message + )) } else { - f.write_fmt(format_args!("({:2x?}) Mac: {:4x?} ENCRYPTED", self.hash, self.mac)) + f.write_fmt(format_args!( + "({:2x?}) Mac: {:4x?} ENCRYPTED", + self.hash, self.mac + )) } } } @@ -224,7 +225,7 @@ impl From<u8> for MessageType { 0x00 => MessageType::Plain, 0x01 => MessageType::CLI, 0x02 => MessageType::SignedPlain, - _ => MessageType::Invalid + _ => MessageType::Invalid, } } } @@ -235,11 +236,17 @@ impl From<u8> for MessageType { mod tests { use std::{collections::HashMap, str::FromStr}; + use crate::{ + packet::*, + packet_content::{PacketContent, PeerToPeerCipher}, + std_identity::KeystoreInput, + string_helper::{NameString, message_string_from_slice, name_string_from_slice}, + text::{ClearText, GroupData, GroupText, MessageType, Text}, + }; + use bytes::Bytes; use chrono::DateTime; use hex::decode; - use bytes::Bytes; use tinyvec::{ArrayVec, array_vec}; - use crate::{packet::*, packet_content::{PacketContent, PeerToPeerCipher}, std_identity::KeystoreInput, string_helper::{NameString, message_string_from_slice, name_string_from_slice}, text::{ClearText, GroupData, GroupText, MessageType, Text}}; #[test] fn text_encrypted() { @@ -250,15 +257,21 @@ mod tests { version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("1234e91c8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9").unwrap()), + 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()), - cleartext: None - }, + destination: 0x12, + source: 0x34, + mac: 0xe91c, + ciphertext: Bytes::copy_from_slice( + &decode("8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9") + .unwrap(), + ), + cleartext: None, + }, cleartext: None, }), incomplete: false, @@ -266,10 +279,13 @@ mod tests { let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", lhs_packet), " Direct | v1 | | [] | | TEXT | (34) -> (12) MAC: e91c ENCRYPTED"); + assert_eq!( + format!("{}", lhs_packet), + " Direct | v1 | | [] | | TEXT | (34) -> (12) MAC: e91c ENCRYPTED" + ); } - #[test] + #[test] fn text_decrypted() { let sample = "0a001234e91c8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9"; @@ -278,26 +294,32 @@ mod tests { version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("1234e91c8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9").unwrap()), + 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()), - cleartext: Some(Bytes::copy_from_slice(b"-\xb3\x07i\x0401|get radio\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")), - }, - cleartext: Some( - ClearText { - timestamp: DateTime::from_timestamp(1762112301, 0).unwrap(), - message_type: MessageType::Plain, - attempts: 0, - sender_hash: 0, - sender: name_string_from_slice(b"Unknown"), - message: message_string_from_slice(b"01|get radio"), - crypto_recipient: 0, - } - ), + destination: 0x12, + source: 0x34, + mac: 0xe91c, + ciphertext: Bytes::copy_from_slice( + &decode("8eb2b815e0eccf6781a3ff1820d0fb130fcfc87b914244fae227d4ad4c752fb9") + .unwrap(), + ), + cleartext: Some(Bytes::copy_from_slice( + b"-\xb3\x07i\x0401|get radio\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", + )), + }, + cleartext: Some(ClearText { + timestamp: DateTime::from_timestamp(1762112301, 0).unwrap(), + message_type: MessageType::Plain, + attempts: 0, + sender_hash: 0, + sender: name_string_from_slice(b"Unknown"), + message: message_string_from_slice(b"01|get radio"), + crypto_recipient: 0, + }), }), incomplete: false, }; @@ -309,7 +331,10 @@ mod tests { let mut rhs_packet = Packet::from_str(sample).unwrap(); rhs_packet.try_decrypt(&keystore); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", rhs_packet), " Direct | v1 | | [] | | TEXT | (34) -> (12) MAC: e91c at: 2025-11-02 19:38:21 UTC, 0 attempts, from Unknown: 01|get radio"); + assert_eq!( + format!("{}", rhs_packet), + " Direct | v1 | | [] | | TEXT | (34) -> (12) MAC: e91c at: 2025-11-02 19:38:21 UTC, 0 attempts, from Unknown: 01|get radio" + ); } #[test] @@ -334,7 +359,10 @@ mod tests { let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", lhs_packet), " Flood | v1 | | [7b, 2c, ... ac, b7] | | GROUP TXT | (11) Mac: 76b8 ENCRYPTED"); + assert_eq!( + format!("{}", lhs_packet), + " Flood | v1 | | [7b, 2c, ... ac, b7] | | GROUP TXT | (11) Mac: 76b8 ENCRYPTED" + ); } #[test] @@ -346,11 +374,17 @@ mod tests { version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("11C3C1354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()), + raw_content: Bytes::copy_from_slice( + &decode("11C3C1354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D") + .unwrap(), + ), content: PacketContent::GroupText(GroupText { hash: 0x11, mac: 0xC3C1, - ciphertext: Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()), + ciphertext: Bytes::copy_from_slice( + &decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D") + .unwrap(), + ), cleartext: Some(ClearText { sender: name_string_from_slice(b"\xF0\x9F\x8C\xB2 Tree"), message: message_string_from_slice(b"\xE2\x98\x81\xEF\xB8\x8F"), @@ -360,9 +394,9 @@ mod tests { sender_hash: 0, crypto_recipient: 0, }), - incomplete: false + incomplete: false, }), - incomplete: false + incomplete: false, }; let mut rhs_packet = Packet::from_str(sample).unwrap(); @@ -370,17 +404,22 @@ mod tests { let keystore = KeystoreInput { identities: HashMap::new(), contacts: HashMap::new(), - groups: HashMap::from([ - ("Public".to_owned(), "8b3387e9c5cdea6ac9e5edbaa115cd72".to_owned()) - ]) - }.compile(); - + groups: HashMap::from([( + "Public".to_owned(), + "8b3387e9c5cdea6ac9e5edbaa115cd72".to_owned(), + )]), + } + .compile(); + _ = rhs_packet.try_decrypt(&keystore); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", lhs_packet), " Flood | v1 | | [] | | GROUP TXT | (11) Mac: c3c1 Group 0: ☁\u{fe0f}"); + assert_eq!( + format!("{}", lhs_packet), + " Flood | v1 | | [] | | GROUP TXT | (11) Mac: c3c1 Group 0: ☁\u{fe0f}" + ); } - + #[test] fn group_data() { let sample = "18079DBB163F3C88707DF4C430C8A06A587CF7E827641C6521A5DE85581C8800793AF4A5497196CB5F24B92A33A9C8AA3BAE4F8C94E8E464849BCBF6333509C3941C47B7ABF85ECFD2FF06DA1A39575D155941F152F63300D2C31B5FAFBDA79637"; @@ -390,15 +429,22 @@ mod tests { version: PayloadVersion::VersionOne, path: array_vec!([u16; 64] => 0x3C, 0x88, 0x70, 0x7D, 0xF4, 0xC4, 0x30, 0xC8, 0xA0, 0x6A, 0x58, 0x7C, 0xF7, 0xE8, 0x27, 0x64, 0x1C, 0x65, 0x21, 0xA5, 0xDE, 0x85, 0x58, 0x1C, 0x88, 0x00, 0x79, 0x3A, 0xF4, 0xA5, 0x49, 0x71, 0x96, 0xCB, 0x5F, 0x24, 0xB9, 0x2A, 0x33, 0xA9, 0xC8, 0xAA, 0x3B, 0xAE, 0x4F, 0x8C, 0x94, 0xE8, 0xE4, 0x64, 0x84, 0x9B, 0xCB, 0xF6, 0x33, 0x35, 0x09, 0xC3, 0x94, 0x1C, 0x47, 0xB7, 0xAB), transport: [0x9d07, 0x16bb], - raw_content: Bytes::copy_from_slice(&decode("F85ECFD2FF06DA1A39575D155941F152F63300D2C31B5FAFBDA79637").unwrap()), + raw_content: Bytes::copy_from_slice( + &decode("F85ECFD2FF06DA1A39575D155941F152F63300D2C31B5FAFBDA79637").unwrap(), + ), content: PacketContent::GroupData(GroupData { - payload: Bytes::copy_from_slice(&decode("F85ECFD2FF06DA1A39575D155941F152F63300D2C31B5FAFBDA79637").unwrap()) + payload: Bytes::copy_from_slice( + &decode("F85ECFD2FF06DA1A39575D155941F152F63300D2C31B5FAFBDA79637").unwrap(), + ), }), - incomplete: false + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", lhs_packet), "T-Flood | v1 | 9d07, 16bb | [3c, 88, ... b7, ab] | | GRP. DATA | Payload: f85ecfd2ff06da1a39575d155941f152f63300d2c31b5fafbda79637"); + assert_eq!( + format!("{}", lhs_packet), + "T-Flood | v1 | 9d07, 16bb | [3c, 88, ... b7, ab] | | GRP. DATA | Payload: f85ecfd2ff06da1a39575d155941f152f63300d2c31b5fafbda79637" + ); } -}
\ No newline at end of file +} |
