diff options
| author | Will Dillon <william@housedillon.com> | 2025-11-08 06:54:10 +0000 |
|---|---|---|
| committer | Will Dillon <william@housedillon.com> | 2025-11-08 06:54:10 +0000 |
| commit | 6ce2b201ae021f055df181fac19ea53b41d77895 (patch) | |
| tree | 7cc778bef6969270ee571d7afd633412bcfddec1 /src | |
| parent | Back over to the laptop (diff) | |
| download | meshcore-rs-6ce2b201ae021f055df181fac19ea53b41d77895.tar.gz meshcore-rs-6ce2b201ae021f055df181fac19ea53b41d77895.zip | |
Getting pretty close to finished
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/packet_analyzer.rs | 67 | ||||
| -rw-r--r-- | src/crypto.rs | 28 | ||||
| -rw-r--r-- | src/identity.rs | 91 | ||||
| -rw-r--r-- | src/packet.rs | 78 | ||||
| -rw-r--r-- | src/packet_content.rs | 255 |
5 files changed, 441 insertions, 78 deletions
diff --git a/src/bin/packet_analyzer.rs b/src/bin/packet_analyzer.rs index ee3c3fe..9348a08 100644 --- a/src/bin/packet_analyzer.rs +++ b/src/bin/packet_analyzer.rs @@ -1,11 +1,15 @@ -use std::path::PathBuf; +use std::{borrow::Cow, path::PathBuf}; +use hex::encode; +use log::{error, trace}; use tokio::fs::File; +use tokio_util::bytes::Bytes; use clap::Parser; use color_eyre::eyre::Result; -use meshcore::{crypto::Keychain, identity::KeystoreInput}; +use meshcore::{identity::{Keystore, KeystoreInput}, packet::Packet}; use pretty_env_logger; -use pcap_file_tokio::pcapng::{Block::*, PcapNgReader, PcapNgWriter, blocks::interface_description::InterfaceDescriptionBlock}; +use pcap_file_tokio::pcapng::PcapNgReader; + #[derive(Parser)] struct AnalyzerArguments { @@ -16,6 +20,16 @@ struct AnalyzerArguments { pcap_file: PathBuf, } +fn process_packet(data: Cow<[u8]>, keystore: &Keystore) { + // Convert the Cow into Bytes so we can do the packet analysis. + let bytes = Bytes::copy_from_slice(&data); + + let mut packet = Packet::from(bytes); + packet.try_decrypt(keystore); + + println!("{}", packet); +} + #[tokio::main] async fn main() -> Result<()> { pretty_env_logger::init(); @@ -25,12 +39,55 @@ async fn main() -> Result<()> { // Attempt to load the identities file from disk and load all the identities let identity_string = std::fs::read_to_string(args.identities_file)?; let keystore_in: KeystoreInput = toml::from_str(&identity_string)?; + let keystore = keystore_in.compile(); + // println!("Keystore: \n{:#?}", keystore); + // Pcapng file for loading packets let pcap_file = File::open(args.pcap_file).await?; - let pcap_reader = PcapNgReader::new(pcap_file).await?; - + let mut pcap_reader = PcapNgReader::new(pcap_file).await?; + + println!(" ROUTE T | v1 | Transp Fl. | Route Summary | I | Pkt. Type | Summary...."); + + // Iterate through the packets in the pacap + // and print out their contents! Wheeeee! + while let Some(pkt) = pcap_reader.next_block().await { + use pcap_file_tokio::pcapng::Block::*; + match pkt { + Ok(SectionHeader(section_header_block)) => { + trace!("SectionHeader: {:#?}", section_header_block); + } + Ok(InterfaceDescription(interface_description_block)) => { + trace!("InterfaceDescription: {:#?}", interface_description_block); + } + Ok(Packet(packet_block)) => { + trace!("Packet: {:#?}", packet_block); + } + Ok(SimplePacket(simple_packet_block)) => { + trace!("SimplePacket: {:#?}", simple_packet_block); + } + Ok(NameResolution(name_resolution_block)) => { + trace!("NameResolution: {:#?}", name_resolution_block); + } + Ok(InterfaceStatistics(interface_statistics_block)) => { + trace!("InterfaceStatistics: {:#?}", interface_statistics_block); + } + Ok(EnhancedPacket(enhanced_packet_block)) => { + trace!("EnhancedPacket: {}", encode(&enhanced_packet_block.data)); + process_packet(enhanced_packet_block.data.clone(), &keystore); + } + Ok(SystemdJournalExport(systemd_journal_export_block)) => { + trace!("SystemdJournalExport: {:#?}", systemd_journal_export_block); + } + Ok(Unknown(unknown_block)) => { + trace!("Unknown: {:#?}", unknown_block); + } + Err(e) => { + error!("Error in Pcap file! {}", e); + } + } + } Ok(()) }
\ No newline at end of file diff --git a/src/crypto.rs b/src/crypto.rs index 8e3afa7..3175501 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -81,6 +81,12 @@ impl std::fmt::Debug for PublicKey { } } +impl PublicKey { + pub fn hash_prefix(&self) -> u8 { + self.0.as_bytes()[0] + } +} + #[derive(Clone)] pub struct SharedSecret(MontgomeryPoint); @@ -104,11 +110,15 @@ impl FromStr for SharedSecret { type Err = MeshcoreCryptoError; fn from_str(s: &str) -> Result<Self, Self::Err> { - let mut slice = [0_u8; 32]; - if decode_to_slice(s, &mut slice).is_err() { + let mut array = [0_u8; 32]; + + // The provided group secrets are only 16 bytes, + // but they're zero-paded to be 32. So, we're + // going to get the hex from the string and copy it in. + if decode_to_slice(s, &mut array[0..16]).is_err() { return Err(MeshcoreCryptoError::TryFromSliceError) } else { - Ok(SharedSecret(MontgomeryPoint(slice))) + Ok(SharedSecret(MontgomeryPoint(array))) } } } @@ -218,6 +228,10 @@ impl SharedSecret { Err(MeshcoreCryptoError::KeyLengthError) } } + + pub fn hash_prefix(&self) -> u8 { + self.0.0[0] + } } // This is just for creating placeholders @@ -297,8 +311,6 @@ impl FromStr for PrivateKey { impl From<&PrivateKey> for PublicKey { fn from(key: &PrivateKey) -> Self { - println!("Scalar: {}", encode(key.0.scalar.to_bytes())); - // let key = key.0.verifying_key(); let key = (key.0.scalar * curve25519_dalek::constants::ED25519_BASEPOINT_POINT).compress(); PublicKey(VerifyingKey::from_bytes(key.as_bytes()).unwrap()) } @@ -311,7 +323,7 @@ impl PrivateKey { } impl SharedSecret { - pub fn mac_then_decrypt(&self, mac: u16, data: Bytes) -> Option<Bytes> { + pub fn mac_then_decrypt(&self, mac: u16, data: &Bytes) -> Option<Bytes> { // Get the MAC of the message and key to check vailidity let our_mac = self.get_hmac(&data); if our_mac != mac { return None } @@ -383,7 +395,7 @@ mod tests { fn hmac() { // Test using a group secret let group_secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap())); - let test_group_secret =SharedSecret::from_str("8b3387e9c5cdea6ac9e5edbaa115cd7200000000000000000000000000000000").unwrap(); + let test_group_secret =SharedSecret::from_str("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(); assert!(group_secret == test_group_secret); // Test using the secret and ciphertext to make a MAC and ensure it matches an example for a group secret @@ -501,7 +513,7 @@ mod tests { let sample_data = Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()); let mac = 0xC3C1; - let cleartext = group_secret.mac_then_decrypt(mac, sample_data).unwrap(); + let cleartext = group_secret.mac_then_decrypt(mac, &sample_data).unwrap(); assert!(cleartext == decode("3757d06800f09f8cb220547265653a20e29881efb88f00000000000000000000").unwrap()); } }
\ No newline at end of file diff --git a/src/identity.rs b/src/identity.rs index 050bfa1..47ec956 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -1,6 +1,7 @@ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, rc::Rc, str::FromStr}; use hex::decode; use serde::{Deserialize, de}; +use tokio_util::bytes::Bytes; use crate::crypto::{MeshcoreCryptoError, PrivateKey, PublicKey, SharedSecret}; #[derive(PartialEq, Debug, Clone, Deserialize)] @@ -11,7 +12,7 @@ use crate::crypto::{MeshcoreCryptoError, PrivateKey, PublicKey, SharedSecret}; /// as this identity when creating messages. pub struct Identity { /// The display name if this user identity. - pub name: String, + pub name: Rc<String>, /// The private key of this identity. There are currently /// no ways in this library to generate a new private key @@ -32,7 +33,7 @@ pub struct Identity { /// used because there are many many hash collisions /// when using just the 1-byte hash prefix. #[serde(skip)] - pub secrets: HashMap<String, SharedSecret> + pub secrets: Vec<(Rc<String>, SharedSecret)> } #[derive(PartialEq, Debug, Clone, Deserialize)] @@ -47,7 +48,7 @@ pub struct Identity { /// before the correct contact is found. pub struct Contact { /// The display name of the contact - pub name: String, + pub name: Rc<String>, /// The provided public key of the remote contact #[serde(deserialize_with = "deserialize_public_key")] @@ -62,7 +63,7 @@ pub struct Contact { /// by whether its secret can correctly decrypt messages. pub struct Group { /// The display name of the group - pub name: String, + pub name: Rc<String>, /// The group's shared secret #[serde(deserialize_with = "deserialize_secret")] @@ -71,44 +72,86 @@ pub struct Group { #[derive(PartialEq, Debug, Clone)] pub struct Keystore { - identities: HashMap<String, Identity>, - contacts: HashMap<String, Contact>, - groups: HashMap<String, Group>, + pub identities: HashMap<Rc<String>, Identity>, + pub contacts: HashMap<Rc<String>, Contact>, + pub groups: HashMap<Rc<String>, Group>, +} + +impl Keystore { + pub fn decrypt_and_id_p2p(&self, source: u8, dest: u8, mac: u16, data: &Bytes) -> Option<(Bytes, Identity, Contact)> { + None + } + + pub fn decrypt_and_id_group(&self, group_hash_prefix: u8, mac: u16, data: &Bytes) -> Option<(Bytes, &Group)> { + for group in self.groups.iter() { + // if group.1.secret.hash_prefix() == group_hash_prefix { + let result = group.1.secret.mac_then_decrypt(mac, data); + if let Some(result) = result { + return Some((result, group.1)); + } + // } + } + + None + } + + pub fn decrypt_anon(&self, dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes) -> Option<(Bytes, Identity)> { + None + } } #[derive(PartialEq, Debug, Clone, Deserialize)] pub struct KeystoreInput { - identities: Vec<Identity>, - contacts: Vec<Contact>, - groups: Vec<Group> + pub(crate) identities: Vec<Identity>, + pub(crate) contacts: Vec<Contact>, + pub(crate) groups: Vec<Group> } impl KeystoreInput { pub fn compile(self) -> Keystore { let mut retval = Keystore { - identities: todo!(), - contacts: todo!(), - groups: todo!() + identities: HashMap::new(), + contacts: HashMap::new(), + groups: HashMap::new() }; // Iterate through the input keystore file // for each one, make sure it's in the map correctly // and use the opportunity to compute the shared keys. - for identity in self.identities.iter_mut() { - identity.public_key = PublicKey::from(identity.public_key); + let identities = self.identities.into_iter().map(|mut i| { + i.public_key = PublicKey::from(&i.private_key); - // Iterate through the contacts and generate shared secrets - for contact in self.contacts.as_i - } + i.secrets = self.contacts.iter().map(|c| { + (c.name.clone(), i.private_key.create_secret(&c.public_key)) + }).collect(); + + (i.name.clone(), i) + }); + + retval.identities = HashMap::from_iter(identities); + + // Create a hash of contacts + let contacts = self.contacts.into_iter().map(|c| { + (c.name.clone(), c) + }); + + retval.contacts = HashMap::from_iter(contacts); + + // Create a hash of groups + let groups = self.groups.into_iter().map(|g| { + (g.name.clone(), g) + }); + retval.groups = HashMap::from_iter(groups); + retval } } fn deserialize_private_key<'de, D>(deserializer: D) -> Result<PrivateKey, D::Error> where D: de::Deserializer<'de> { - let s: &str = de::Deserialize::deserialize(deserializer)?; - match PrivateKey::from_str(s) { + let s: String = de::Deserialize::deserialize(deserializer)?; + match PrivateKey::from_str(&s) { Ok(key) => Ok(key), Err(e) => { Err(de::Error::custom(format!("{}", e))) @@ -118,7 +161,7 @@ fn deserialize_private_key<'de, D>(deserializer: D) -> Result<PrivateKey, D::Err fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error> where D: de::Deserializer<'de> { - let s: &str = de::Deserialize::deserialize(deserializer)?; + let s: String = de::Deserialize::deserialize(deserializer)?; match decode(s) { Err(hex::FromHexError::InvalidHexCharacter { c, index }) => { @@ -147,9 +190,9 @@ fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error fn deserialize_secret<'de, D>(deserializer: D) -> Result<SharedSecret, D::Error> where D: de::Deserializer<'de> { - let s: &str = de::Deserialize::deserialize(deserializer)?; + let s: String = de::Deserialize::deserialize(deserializer)?; - match SharedSecret::from_str(s) { + match SharedSecret::from_str(&s) { Ok(secret) => Ok(secret), Err(e) => { Err(de::Error::custom(format!("{}", e))) diff --git a/src/packet.rs b/src/packet.rs index c4e3809..ba98ac9 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,9 +1,9 @@ -use std::str::FromStr; +use std::{fmt::{Debug, Display}, str::FromStr}; use hex::decode; use tokio_util::bytes::{Buf, Bytes}; use structdiff::{Difference, StructDiff}; -use crate::{crypto::SharedSecret, packet_content::PacketContent}; +use crate::{crypto::SharedSecret, identity::Keystore, packet_content::PacketContent}; #[derive(PartialEq, Debug, Clone, Difference)] #[difference(expose)] @@ -132,28 +132,60 @@ impl Default for Packet { } } +impl Display for Packet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.route_type, f)?; + f.write_str(" | ")?; + std::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!(" [{:2x?}] | ", self.path[0])), + 2 => f.write_fmt(format_args!(" [{:2x?}, {:2x?}] | ", self.path[0], self.path[1])), + 3 => f.write_fmt(format_args!(" [{:2x?}, {:2x?}, {:2x?}] | ", self.path[0], self.path[1], self.path[2])), + _ => f.write_fmt(format_args!(" [{:2x?}, {:2x?}, ... {:2x?}, {:2x?}] | ", 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(" | ") + }?; + + std::fmt::Display::fmt(&self.content, f) + } +} + impl Packet { - pub fn try_decrypt(&mut self, key: SharedSecret) -> bool { - + pub fn try_decrypt(&mut self, keystore: &Keystore) -> bool { match self.content { // Encrypted packet types PacketContent::Path(ref mut path) => { - path.cipher.try_decrypt(key) + path.cipher.try_decrypt(keystore) }, PacketContent::Request(ref mut request) => { - request.cipher.try_decrypt(key) + request.cipher.try_decrypt(keystore) }, PacketContent::Response(ref mut response) => { - response.cipher.try_decrypt(key) + response.cipher.try_decrypt(keystore) }, PacketContent::Text(ref mut text) => { - text.cipher.try_decrypt(key) + text.cipher.try_decrypt(keystore) }, PacketContent::AnonReq(ref mut anon_req) => { - let decrypt_reault = key.mac_then_decrypt( + let decrypt_reault = keystore.decrypt_anon( + anon_req.dest, + &anon_req.public_key, anon_req.mac, - anon_req.ciphertext.clone() + &anon_req.ciphertext ); if let Some(cleartext) = decrypt_reault { @@ -164,7 +196,7 @@ impl Packet { } PacketContent::GroupText(ref mut group_text) => { - group_text.try_decrypt(key) + group_text.try_decrypt(keystore) } // None of the other packets implement any encryption @@ -182,6 +214,18 @@ pub enum RouteType { Invalid, } +impl Display for RouteType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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<u8> for RouteType { fn from(value: u8) -> Self { match value & 0x03 { @@ -218,6 +262,18 @@ impl From<u8> for PayloadVersion { } } +impl Display for PayloadVersion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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"), + } + } +} + #[allow(dead_code)] pub(crate) fn print_compare(lhs: Packet, rhs: Packet) -> String { let mut output_string = format!("Left hand side: \n{:#?}\nRight hand side: \n{:#?}\nDifferences: \n", lhs, rhs); 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()), |
