diff options
Diffstat (limited to 'src/packet_content.rs')
| -rw-r--r-- | src/packet_content.rs | 255 |
1 files changed, 225 insertions, 30 deletions
diff --git a/src/packet_content.rs b/src/packet_content.rs index 3897f2d..f19d36e 100644 --- a/src/packet_content.rs +++ b/src/packet_content.rs @@ -1,7 +1,10 @@ +use std::{fmt::{Debug, Display}, rc::Rc}; + use chrono::{DateTime, Local, Utc}; +use hex::encode; use tokio_util::bytes::{Buf, Bytes}; use structdiff::{Difference, StructDiff}; -use crate::crypto::{PublicKey, SharedSecret}; +use crate::{crypto::PublicKey, identity::Keystore}; #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] @@ -23,6 +26,24 @@ pub enum PacketContent { } impl PacketContent { + fn justified_name(&self) -> &str{ + match self { + PacketContent::Request(_) => " REQUEST | ", + PacketContent::Response(_) => " RESPONSE | ", + PacketContent::Text(_) => " TEXT | ", + PacketContent::Ack(_) => " ACK | ", + PacketContent::Advert(_) => " ADVERT | ", + PacketContent::GroupText(_) => " GROUP TXT | ", + PacketContent::GroupData(_) => " GRP. DATA | ", + PacketContent::AnonReq(_) => " ANON REQ. | ", + PacketContent::Path(_) => " PATH | ", + PacketContent::Trace(_) => " TRACE | ", + PacketContent::Multipart(_) => " MULTIPART | ", + PacketContent::Raw(_) => " RAW | ", + PacketContent::Invalid => " INVALID | ", + } + } + pub fn new(header: u8, bytes: Bytes) -> PacketContent { // Specialize based on the Payload Type from the header match (header & 0x3C) >> 2 { @@ -44,6 +65,28 @@ impl PacketContent { } } +impl Display for PacketContent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.justified_name())?; + + match self { + PacketContent::Request(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Response(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Text(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Ack(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Advert(c) => std::fmt::Display::fmt(&c, f), + PacketContent::GroupText(c) => std::fmt::Display::fmt(&c, f), + PacketContent::GroupData(c) => std::fmt::Display::fmt(&c, f), + PacketContent::AnonReq(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Path(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Trace(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Multipart(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Raw(c) => std::fmt::Display::fmt(&c, f), + PacketContent::Invalid => f.write_str("INVALID") + } + } +} + #[derive(PartialEq, Debug, Clone)] pub enum NodeType { Chat, @@ -101,10 +144,15 @@ impl From<Bytes> for PeerToPeerCipher { } impl PeerToPeerCipher { - pub fn try_decrypt(&mut self, key: SharedSecret) -> bool { - let decrypt = key.mac_then_decrypt(self.mac, self.ciphertext.clone()); - - if let Some(cleartext) = decrypt { + pub fn try_decrypt(&mut self, keystore: &Keystore) -> bool { + let decrypt = keystore.decrypt_and_id_p2p( + self.source, + self.destination, + self.mac, + &self.ciphertext + ); + + if let Some((cleartext, _, _)) = decrypt { self.cleartext = Some(cleartext); true } else { @@ -121,13 +169,28 @@ pub struct Request { impl From<Bytes> for Request { fn from(value: Bytes) -> Self { - Request { cipher: PeerToPeerCipher::from(value) } } } +impl Display for Request { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", + self.cipher.source, + self.cipher.destination, + self.cipher.mac + ))?; + + if let Some(cleartext) = &self.cipher.cleartext { + f.write_fmt(format_args!("TODO")) + } else { + f.write_str("ENCRYPTED") + } + } +} + #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct Response { @@ -136,14 +199,28 @@ pub struct Response { impl From<Bytes> for Response { fn from(value: Bytes) -> Self { - - Response { cipher: PeerToPeerCipher::from(value) } } } +impl Display for Response { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", + self.cipher.source, + self.cipher.destination, + self.cipher.mac + ))?; + + if let Some(cleartext) = &self.cipher.cleartext { + f.write_fmt(format_args!("")) + } else { + f.write_str("ENCRYPTED") + } + } +} + #[derive(PartialEq, Debug, Clone)] pub struct Text { pub(crate) cipher: PeerToPeerCipher, @@ -155,6 +232,22 @@ impl From<Bytes> for Text { } } +impl Display for Text { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", + self.cipher.source, + self.cipher.destination, + self.cipher.mac + ))?; + + if let Some(cleartext) = &self.cipher.cleartext { + f.write_fmt(format_args!("Cleartext implementation TODO")) + } else { + f.write_str("ENCRYPTED") + } + } +} + #[derive(PartialEq, Debug, Clone)] pub struct Ack { checksum: u32 @@ -167,6 +260,12 @@ impl From<Bytes> for Ack { } } +impl Display for Ack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Checksum: {:4x?}", self.checksum)) + } +} + #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct Advert { @@ -256,6 +355,25 @@ impl From<Bytes> for Advert { } } +impl Display for Advert { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.node_type.fmt(f)?; + f.write_str(" \"")?; + f.write_str(&self.name)?; + f.write_fmt(format_args!("\" ({:2x?}) at: ", self.public_key.hash_prefix()))?; + std::fmt::Display::fmt(&self.timestamp, f)?; + + match (self.latitude, self.longitude) { + (Some(lat), Some(lon)) => { + f.write_fmt(format_args!(" location: {}, {}", lat, lon))?; + }, + _ => {}, + } + + Ok(()) + } +} + #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct GroupText { @@ -292,15 +410,17 @@ impl From<Bytes> for GroupText { } impl GroupText { - pub fn try_decrypt(&mut self, key: SharedSecret) -> bool { - let decrypt_reault = key.mac_then_decrypt( + pub fn try_decrypt(&mut self, keysore: &Keystore) -> bool { + let decrypt_result = keysore.decrypt_and_id_group( + self.hash, self.mac, - self.ciphertext.clone() + &self.ciphertext ); - if let Some(cleartext) = decrypt_reault { - self.cleartext = Some(ClearText::from(cleartext)); - + if let Some((cleartext, group)) = decrypt_result { + let mut cleartext = ClearText::from(cleartext); + cleartext.crypto_recipient = group.name.clone(); + self.cleartext = Some(cleartext); true } else { false @@ -308,6 +428,21 @@ impl GroupText { } } +impl Display for GroupText { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(cleartext) = &self.cleartext { + f.write_fmt(format_args!("({:2x?}) Mac: {:4x?} Group {}: {}", + self.hash, + self.mac, + cleartext.crypto_recipient, + cleartext.message.replace("\n", "\\n")) + ) + } else { + f.write_fmt(format_args!("({:2x?}) Mac: {:4x?} ENCRYPTED", self.hash, self.mac)) + } + } +} + #[derive(PartialEq, Debug, Clone)] pub enum MessageType { Plain, @@ -331,12 +466,14 @@ impl From<u8> for MessageType { #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct ClearText { - timestamp: DateTime<Utc>, - message_type: MessageType, - attempts: u8, - sender_hash: u32, - sender: Option<String>, - message: String, + pub timestamp: DateTime<Utc>, + pub message_type: MessageType, + pub attempts: u8, + pub sender_hash: u32, + pub sender: Option<String>, + pub message: String, + + pub crypto_recipient: Rc<String> } impl From<Bytes> for ClearText { @@ -350,6 +487,7 @@ impl From<Bytes> for ClearText { sender_hash: 0, sender: None, message: "".to_string(), + crypto_recipient: Rc::new("".to_string()), }; // Just check for the whole fixed-size part at once @@ -394,14 +532,20 @@ impl From<Bytes> for GroupData { } } +impl Display for GroupData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Payload: {}", encode(&self.payload))) + } +} + #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct AnonReq { - destination: u8, - public_key: PublicKey, - pub(crate) mac: u16, - pub(crate) ciphertext: Bytes, - request: Option<ClearAnonRequest>, + pub dest: u8, + pub public_key: PublicKey, + pub mac: u16, + pub ciphertext: Bytes, + pub request: Option<ClearAnonRequest>, incomplete: bool } @@ -410,7 +554,7 @@ impl From<Bytes> for AnonReq { let mut bytes = value; let mut anon_req = AnonReq { - destination: 0x00, + dest: 0x00, public_key: PublicKey::default(), mac: 0x0000, ciphertext: Bytes::new(), @@ -419,7 +563,7 @@ impl From<Bytes> for AnonReq { }; if bytes.is_empty() { return anon_req; } - anon_req.destination = bytes.get_u8(); + anon_req.dest = bytes.get_u8(); if bytes.len() < 32 { return anon_req; } if let Ok(pub_key) = PublicKey::try_from(bytes.split_to(32)) { @@ -435,6 +579,18 @@ impl From<Bytes> for AnonReq { } } +impl Display for AnonReq { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", self.public_key.hash_prefix(), self.dest, self.mac))?; + + if let Some(cleartext) = &self.request { + f.write_fmt(format_args!("at: {} password: \"{}\"", cleartext.timestamp, cleartext.password)) + } else { + f.write_str("ENCRYPTED") + } + } +} + #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] pub struct ClearAnonRequest { @@ -469,7 +625,6 @@ impl From<Bytes> for ClearAnonRequest { } } - #[derive(PartialEq, Debug, Clone)] pub struct Path { pub(crate) cipher: PeerToPeerCipher, @@ -483,6 +638,12 @@ impl From<Bytes> for Path { } } +impl Display for Path { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", self.cipher.source, self.cipher.destination, self.cipher.mac)) + } +} + #[derive(PartialEq, Debug, Clone)] pub struct Trace { tag: u32, @@ -518,6 +679,12 @@ impl From<Bytes> for Trace { } } +impl Display for Trace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Todo")) + } +} + #[derive(PartialEq, Debug, Clone)] pub struct MultiPart { payload: Bytes @@ -529,18 +696,33 @@ impl From<Bytes> for MultiPart { } } +impl Display for MultiPart { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Todo")) + } +} + #[derive(PartialEq, Debug, Clone)] pub struct Raw { pub(crate) bytes: Bytes } +impl Display for Raw { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self, f) + } +} + #[cfg(test)] mod tests { + use std::rc::Rc; use std::str::FromStr; use chrono::DateTime; use hex::decode; + use crate::identity::Group; + use crate::identity::KeystoreInput; use crate::packet::*; use crate::crypto::*; @@ -791,6 +973,7 @@ mod tests { message_type: MessageType::Plain, attempts: 0, sender_hash: 0, + crypto_recipient: Rc::new("Public".to_owned()), }), incomplete: false }), @@ -798,7 +981,19 @@ mod tests { }; let mut rhs_packet = Packet::from_str(sample).unwrap(); - _ = rhs_packet.try_decrypt(SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap()))); + + let keystore = KeystoreInput { + identities: vec![], + contacts: vec![], + groups: vec![ + Group { + name: Rc::new("Public".to_owned()), + secret: SharedSecret::from_str("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap() + } + ] + }.compile(); + + _ = rhs_packet.try_decrypt(&keystore); assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); } @@ -854,7 +1049,7 @@ mod tests { transport: [0, 0], raw_content: Bytes::copy_from_slice(&decode("1234569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8fd2db15a7138098557cc291928b4358fa7522ddd41d35c99fb78f0def2b3e673d73d2").unwrap()), content: PacketContent::AnonReq(AnonReq { - destination: 0x12, + dest: 0x12, public_key: PublicKey::try_from(Bytes::copy_from_slice(&decode("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap())).unwrap(), mac: 0xd2db, ciphertext: Bytes::copy_from_slice(&decode("15a7138098557cc291928b4358fa7522ddd41d35c99fb78f0def2b3e673d73d2").unwrap()), |
