aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWill Dillon <william@housedillon.com>2025-11-08 06:54:10 +0000
committerWill Dillon <william@housedillon.com>2025-11-08 06:54:10 +0000
commit6ce2b201ae021f055df181fac19ea53b41d77895 (patch)
tree7cc778bef6969270ee571d7afd633412bcfddec1 /src
parentBack over to the laptop (diff)
downloadmeshcore-rs-6ce2b201ae021f055df181fac19ea53b41d77895.tar.gz
meshcore-rs-6ce2b201ae021f055df181fac19ea53b41d77895.zip
Getting pretty close to finished
Diffstat (limited to 'src')
-rw-r--r--src/bin/packet_analyzer.rs67
-rw-r--r--src/crypto.rs28
-rw-r--r--src/identity.rs91
-rw-r--r--src/packet.rs78
-rw-r--r--src/packet_content.rs255
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()),