diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ack.rs | 24 | ||||
| -rw-r--r-- | src/advert.rs | 65 | ||||
| -rw-r--r-- | src/anon_req.rs | 64 | ||||
| -rw-r--r-- | src/bin/packet_analyzer.rs | 14 | ||||
| -rw-r--r-- | src/crypto.rs | 236 | ||||
| -rw-r--r-- | src/lib.rs | 10 | ||||
| -rw-r--r-- | src/multipart.rs | 18 | ||||
| -rw-r--r-- | src/no_std_identity.rs | 43 | ||||
| -rw-r--r-- | src/packet.rs | 197 | ||||
| -rw-r--r-- | src/packet_content.rs | 120 | ||||
| -rw-r--r-- | src/path.rs | 39 | ||||
| -rw-r--r-- | src/request.rs | 89 | ||||
| -rw-r--r-- | src/response.rs | 79 | ||||
| -rw-r--r-- | src/std_identity.rs | 260 | ||||
| -rw-r--r-- | src/string_helper.rs | 15 | ||||
| -rw-r--r-- | src/text.rs | 208 | ||||
| -rw-r--r-- | src/trace.rs | 34 |
17 files changed, 950 insertions, 565 deletions
@@ -1,15 +1,16 @@ - use bytes::{Buf, Bytes}; #[derive(PartialEq, Debug, Clone)] pub struct Ack { - checksum: u32 + checksum: u32, } impl From<Bytes> for Ack { fn from(value: Bytes) -> Self { let mut bytes = value; - Ack { checksum: bytes.get_u32() } + Ack { + checksum: bytes.get_u32(), + } } } @@ -21,11 +22,11 @@ impl core::fmt::Display for Ack { #[cfg(test)] mod tests { + use crate::{ack::Ack, packet::*, packet_content::PacketContent}; + use bytes::Bytes; use core::str::FromStr; use hex::decode; - use bytes::Bytes; use tinyvec::array_vec; - use crate::{ack::Ack, packet::*, packet_content::PacketContent}; #[test] fn ack() { @@ -38,9 +39,9 @@ mod tests { transport: [0, 0], raw_content: Bytes::copy_from_slice(&decode("24F3214D").unwrap()), content: PacketContent::Ack(Ack { - checksum: 0x24F3214D - }), - incomplete: false + checksum: 0x24F3214D, + }), + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); @@ -49,9 +50,10 @@ mod tests { #[test] fn display() { - let ack = Ack { checksum: 0x24F3214D }; + let ack = Ack { + checksum: 0x24F3214D, + }; println!("{}", ack); assert_eq!(format!("{}", ack), "Checksum: 24f3214d"); } - -}
\ No newline at end of file +} diff --git a/src/advert.rs b/src/advert.rs index 24533cf..c37b491 100644 --- a/src/advert.rs +++ b/src/advert.rs @@ -23,7 +23,6 @@ impl Advert { let mut name_split = bytes.chunk().split(|c| *c == 0); if let Some(name) = name_split.next() { - if let Ok(name) = NameString::from_utf8(name.to_vec()) { Some(name) } else { @@ -39,7 +38,6 @@ impl Advert { let mut name_split = bytes.chunk().split(|c| *c == 0); if let Some(name) = name_split.next() { - if let Ok(name) = NameString::from_utf8(name) { Some(name) } else { @@ -78,42 +76,56 @@ impl From<Bytes> for Advert { let mut advert = Advert::default(); - if bytes.len() < 32 { return advert } + if bytes.len() < 32 { + return advert; + } if let Ok(key) = PublicKey::try_from(bytes.split_to(32)) { advert.public_key = key; } else { - return advert + return advert; } - if bytes.len() < 4 { return advert } + if bytes.len() < 4 { + return advert; + } if let Some(time) = DateTime::from_timestamp(bytes.get_u32_le() as i64, 0) { advert.timestamp = time; } - if bytes.len() < 64 { return advert } + if bytes.len() < 64 { + return advert; + } _ = bytes.try_copy_to_slice(&mut advert.signature); - if bytes.is_empty() { return advert } + if bytes.is_empty() { + return advert; + } let flags = bytes.get_u8(); advert.node_type = NodeType::from(flags); if (flags & 0x10) != 0 { // The location is 8 bytes (4 each for lat and lon) - if bytes.len() < 8 { return advert } + if bytes.len() < 8 { + return advert; + } - advert.latitude = Some(bytes.get_i32_le() as f32 / 1_000_000.0); + advert.latitude = Some(bytes.get_i32_le() as f32 / 1_000_000.0); advert.longitude = Some(bytes.get_i32_le() as f32 / 1_000_000.0); } if (flags & 0x20) != 0 { // Feature 1 is 2 bytes when it's included - if bytes.len() < 2 { return advert } + if bytes.len() < 2 { + return advert; + } advert.feature1 = Some(bytes.get_u16_le()); } if (flags & 0x40) != 0 { // Feature 2 is the same - if bytes.len() < 2 { return advert } + if bytes.len() < 2 { + return advert; + } advert.feature2 = Some(bytes.get_u16_le()); } @@ -124,7 +136,7 @@ impl From<Bytes> for Advert { // Find only the bytes (characters) before the first null. advert.name = Self::name_from_bytes(bytes); - + advert } } @@ -140,14 +152,17 @@ impl core::fmt::Display for Advert { f.write_str("<no name>")?; } - f.write_fmt(format_args!("\" ({:2x?}) at: ", self.public_key.hash_prefix() >> 24))?; + f.write_fmt(format_args!( + "\" ({:2x?}) at: ", + self.public_key.hash_prefix() >> 24 + ))?; core::fmt::Display::fmt(&self.timestamp, f)?; match (self.latitude, self.longitude) { (Some(lat), Some(lon)) => { f.write_fmt(format_args!(" location: {}, {}", lat, lon))?; - }, - _ => {}, + } + _ => {} } Ok(()) @@ -163,8 +178,8 @@ mod tests { use hex::decode; use tinyvec::ArrayVec; - use crate::packet::*; use crate::crypto::*; + use crate::packet::*; use crate::packet_content::PacketContent; use crate::string_helper::name_string_from_slice; @@ -202,7 +217,7 @@ mod tests { feature2: None, name: Some(name_string_from_slice(b"HOWL")), }), - incomplete: false, + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); @@ -234,21 +249,23 @@ mod tests { feature2: None, name: Some(name_string_from_slice(b"hpux735")), }), - incomplete: false, + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); } - #[test] fn display() { let mut signature_slice = [0 as u8; 64]; hex::decode_to_slice("d2d976b687a506dd5325ef526bf3eb52ae687277fcbde9969a5b0087e0eb0f7c1760a50c6a88bec13cc30a2a9b681d713166515e3bbc2bc27f20c0e4d7b67e08", &mut signature_slice).unwrap(); let advert = Advert { - public_key: PublicKey::from_str("460728508c17ef336412a223144d3a623215162682045c44fef7241af0161923").unwrap(), + public_key: PublicKey::from_str( + "460728508c17ef336412a223144d3a623215162682045c44fef7241af0161923", + ) + .unwrap(), timestamp: DateTime::from_timestamp(1762111443, 0).unwrap(), signature: signature_slice, node_type: NodeType::Chat, @@ -260,7 +277,9 @@ mod tests { }; #[cfg(feature = "std")] - assert_eq!(format!("{}", advert), "Chat \"HOWL\" (46) at: 2025-11-02 19:24:03 UTC location: 47.98286, -122.132286"); + assert_eq!( + format!("{}", advert), + "Chat \"HOWL\" (46) at: 2025-11-02 19:24:03 UTC location: 47.98286, -122.132286" + ); } - -}
\ No newline at end of file +} diff --git a/src/anon_req.rs b/src/anon_req.rs index 60d58e4..acc27b6 100644 --- a/src/anon_req.rs +++ b/src/anon_req.rs @@ -1,6 +1,10 @@ -use chrono::{DateTime, Utc}; +use crate::{ + MeshcoreStringError, + crypto::PublicKey, + string_helper::{PasswordString, password_string_from_slice}, +}; use bytes::{Buf, Bytes}; -use crate::{MeshcoreStringError, crypto::PublicKey, string_helper::{PasswordString, password_string_from_slice}}; +use chrono::{DateTime, Utc}; #[derive(PartialEq, Clone, core::fmt::Debug)] pub struct AnonReq { @@ -9,7 +13,7 @@ pub struct AnonReq { pub mac: u16, pub ciphertext: Bytes, pub request: Option<ClearAnonRequest>, - incomplete: bool + incomplete: bool, } impl AnonReq { @@ -36,15 +40,21 @@ impl From<Bytes> for AnonReq { incomplete: false, }; - if bytes.is_empty() { return anon_req; } + if bytes.is_empty() { + return anon_req; + } anon_req.dest = bytes.get_u8(); - if bytes.len() < 32 { return anon_req; } + if bytes.len() < 32 { + return anon_req; + } if let Ok(pub_key) = PublicKey::try_from(bytes.split_to(32)) { anon_req.public_key = pub_key; } - if bytes.len() < 2 { return anon_req; } + if bytes.len() < 2 { + return anon_req; + } anon_req.mac = bytes.get_u16(); anon_req.ciphertext = bytes; @@ -55,10 +65,18 @@ impl From<Bytes> for AnonReq { impl core::fmt::Display for AnonReq { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ", self.public_key.hash_prefix() >> 24, self.dest, self.mac))?; + f.write_fmt(format_args!( + "({:2x?}) -> ({:2x?}) MAC: {:4x?} ", + self.public_key.hash_prefix() >> 24, + self.dest, + self.mac + ))?; if let Some(cleartext) = &self.request { - f.write_fmt(format_args!("at: {} password: \"{}\"", cleartext.timestamp, cleartext.password)) + f.write_fmt(format_args!( + "at: {} password: \"{}\"", + cleartext.timestamp, cleartext.password + )) } else { f.write_str("ENCRYPTED") } @@ -81,16 +99,18 @@ impl From<Bytes> for ClearAnonRequest { // and never changes, and it exercised extensively in tests. timestamp: DateTime::from_timestamp(0, 0).unwrap(), sync_timestamp: None, - password: PasswordString::new() + password: PasswordString::new(), }; // Just check for the whole fixed-size part at once - if bytes.len() < 4 { return anon_req } + if bytes.len() < 4 { + return anon_req; + } if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32_le() as i64, 0) { anon_req.timestamp = timestamp; } - // Strip-off any null characters after the password + // Strip-off any null characters after the password if let Some(pass) = bytes.split(|b| *b == 0).next() { anon_req.password = password_string_from_slice(pass); } @@ -102,15 +122,15 @@ impl From<Bytes> for ClearAnonRequest { // Tests for std operations #[cfg(test)] mod tests { + use super::*; + use crate::crypto::*; + use crate::packet::*; + use crate::packet_content::PacketContent; + use crate::std_identity::KeystoreInput; + use hex::decode; use std::collections::HashMap; use std::str::FromStr; - use hex::decode; use tinyvec::ArrayVec; - use crate::std_identity::KeystoreInput; - use crate::packet::*; - use crate::crypto::*; - use crate::packet_content::PacketContent; - use super::*; #[test] fn anon_req() { @@ -155,13 +175,15 @@ mod tests { assert_eq!(lhs_packet, rhs_packet); println!("\"{}\"", rhs_packet); - assert_eq!(format!("{}", rhs_packet), " Flood | v1 | | [] | | ANON REQ. | (12) -> (34) MAC: 4e7b ENCRYPTED"); + assert_eq!( + format!("{}", rhs_packet), + " Flood | v1 | | [] | | ANON REQ. | (12) -> (34) MAC: 4e7b ENCRYPTED" + ); rhs_packet.try_decrypt(&keystore); - + let lhs_string = format!("{}", rhs_packet); let rhs_string = " Flood | v1 | | [] | | ANON REQ. | (12) -> (34) MAC: 4e7b at: 2025-11-13 17:21:13 UTC password: \"12345\""; assert_eq!(lhs_string, rhs_string); } - -}
\ No newline at end of file +} diff --git a/src/bin/packet_analyzer.rs b/src/bin/packet_analyzer.rs index 54d2f5b..323abbe 100644 --- a/src/bin/packet_analyzer.rs +++ b/src/bin/packet_analyzer.rs @@ -1,15 +1,17 @@ -use std::{borrow::Cow, path::PathBuf}; +use bytes::Bytes; use hex::encode; use log::{error, trace}; +use std::{borrow::Cow, path::PathBuf}; use tokio::fs::File; -use bytes::Bytes; use clap::Parser; use color_eyre::eyre::Result; -use meshcore::{std_identity::{Keystore, KeystoreInput}, packet::Packet}; -use pretty_env_logger; +use meshcore::{ + packet::Packet, + std_identity::{Keystore, KeystoreInput}, +}; use pcap_file_tokio::pcapng::PcapNgReader; - +use pretty_env_logger; #[derive(Parser)] struct AnalyzerArguments { @@ -87,4 +89,4 @@ async fn main() -> Result<()> { } Ok(()) -}
\ No newline at end of file +} diff --git a/src/crypto.rs b/src/crypto.rs index cd184dc..acbb464 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,27 +1,42 @@ - // This seems to be an absolute nightmare. GenericArray sucks // but I can't seem to figure out how to pull it out of this // stack of software +use aes::Aes128; #[allow(deprecated)] -use aes::cipher::{BlockEncrypt, BlockDecrypt, generic_array::GenericArray}; +use aes::cipher::{BlockDecrypt, BlockEncrypt, generic_array::GenericArray}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; use curve25519_dalek::MontgomeryPoint; -use aes::Aes128; -use sha2::{Sha256}; -use hmac::{Hmac, Mac}; use ed25519_dalek::{VerifyingKey, hazmat::ExpandedSecretKey}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; +use hmac::{Hmac, Mac}; +use sha2::Sha256; use crate::string_helper::NameString; - type HmacSha256 = Hmac<Sha256>; pub trait Keystore { - fn decrypt_and_id_p2p(&self, source: u8, _dest: u8, mac: u16, data: &Bytes) -> Option<(Bytes, u32, u32)>; - - fn decrypt_and_id_group(&self, group_hash_prefix: u8, mac: u16, data: &Bytes) -> Option<(Bytes, Option<NameString>)>; - - fn decrypt_anon(&self, dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes) -> Option<(Bytes, u32)>; + fn decrypt_and_id_p2p( + &self, + source: u8, + _dest: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, u32, u32)>; + + fn decrypt_and_id_group( + &self, + group_hash_prefix: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, Option<NameString>)>; + + fn decrypt_anon( + &self, + dest: u8, + pub_key: &PublicKey, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, u32)>; } #[derive(Debug, PartialEq)] @@ -32,7 +47,6 @@ pub enum MeshcoreCryptoError { KeyCreationError, } - impl core::error::Error for MeshcoreCryptoError {} impl core::fmt::Display for MeshcoreCryptoError { @@ -51,7 +65,9 @@ pub struct PrivateKey(ExpandedSecretKey); impl core::fmt::Debug for PrivateKey { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("PrivateKey").field(&hex::encode(self.0.scalar.as_bytes())).finish() + f.debug_tuple("PrivateKey") + .field(&hex::encode(self.0.scalar.as_bytes())) + .finish() } } @@ -59,7 +75,7 @@ impl Clone for PrivateKey { fn clone(&self) -> Self { Self(ExpandedSecretKey { scalar: self.0.scalar, - hash_prefix: self.0.hash_prefix + hash_prefix: self.0.hash_prefix, }) } } @@ -68,8 +84,8 @@ impl Default for PrivateKey { fn default() -> Self { // To make a key whole-cloth, we need to start with a SigningKey made // using a good RNG. Then we can use that to make an ExpandedSecretKey. - use rand::rngs::OsRng; use ed25519_dalek::SigningKey; + use rand::rngs::OsRng; // This seems like the same sequence of steps that's used with ed25519 itself. // I have to copy-pasta it because I don't see a way to do it directly with @@ -97,7 +113,9 @@ pub struct PublicKey(ed25519_dalek::VerifyingKey); impl core::fmt::Debug for PublicKey { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("PublicKey").field(&hex::encode(self.0.as_bytes())).finish() + f.debug_tuple("PublicKey") + .field(&hex::encode(self.0.as_bytes())) + .finish() } } @@ -116,7 +134,7 @@ impl PartialEq for SharedSecret { fn eq(&self, other: &Self) -> bool { self.0.as_bytes() == other.0.as_bytes() } - + fn ne(&self, other: &Self) -> bool { !self.eq(other) } @@ -124,7 +142,9 @@ impl PartialEq for SharedSecret { impl core::fmt::Debug for SharedSecret { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("SharedSecret").field(&hex::encode(self.0.as_bytes())).finish() + f.debug_tuple("SharedSecret") + .field(&hex::encode(self.0.as_bytes())) + .finish() } } @@ -138,7 +158,7 @@ impl core::str::FromStr for SharedSecret { // but they're zero-paded to be 32. So, we're // going to get the hex from the string and copy it in. if hex::decode_to_slice(s, &mut array[0..16]).is_err() { - return Err(MeshcoreCryptoError::TryFromSliceError) + return Err(MeshcoreCryptoError::TryFromSliceError); } else { Ok(SharedSecret(MontgomeryPoint(array))) } @@ -149,8 +169,9 @@ impl TryFrom<Bytes> for SharedSecret { type Error = MeshcoreCryptoError; fn try_from(value: Bytes) -> Result<Self, Self::Error> { - if value.len() != 32 { Err(MeshcoreCryptoError::KeyLengthError) } - else { + if value.len() != 32 { + Err(MeshcoreCryptoError::KeyLengthError) + } else { let mut value = value; let mut retval = Self(MontgomeryPoint([0_u8; 32])); value.copy_to_slice(&mut retval.0.0); @@ -190,8 +211,8 @@ impl SharedSecret { // At this point, we're sure that the key is an appropriate size for // the Hmac. So, I don't think we should complicate the API with making // this failable. - let mut mac = HmacSha256::new_from_slice(self.0.as_bytes()) - .expect("Programming error in hmac"); + let mut mac = + HmacSha256::new_from_slice(self.0.as_bytes()).expect("Programming error in hmac"); mac.update(&ciphertext); let result = mac.finalize(); let mut bytes = Bytes::copy_from_slice(&result.into_bytes()); @@ -219,7 +240,7 @@ impl SharedSecret { text.reserve(padding); text.put_bytes(0, padding); } - + let chunks = text.chunks_exact_mut(chunk_size); for chunk in chunks { #[allow(deprecated)] @@ -262,7 +283,7 @@ impl SharedSecret { text.reserve(padding); text.put_bytes(0, padding); } - + let chunks = text.chunks_exact_mut(chunk_size); for chunk in chunks { #[allow(deprecated)] @@ -300,13 +321,13 @@ impl TryFrom<Bytes> for PublicKey { fn try_from(mut value: Bytes) -> Result<Self, Self::Error> { if value.len() < 32 { - return Err(MeshcoreCryptoError::KeyLengthError) + return Err(MeshcoreCryptoError::KeyLengthError); } let mut slice = [0_u8; 32]; if value.try_copy_to_slice(&mut slice).is_err() { - return Err(MeshcoreCryptoError::KeyLengthError) + return Err(MeshcoreCryptoError::KeyLengthError); } if let Ok(key) = VerifyingKey::from_bytes(&slice) { @@ -315,7 +336,6 @@ impl TryFrom<Bytes> for PublicKey { Err(MeshcoreCryptoError::KeyCreationError) } } - } impl core::str::FromStr for PublicKey { @@ -371,7 +391,9 @@ impl SharedSecret { 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 } + if our_mac != mac { + return None; + } // Attempt to decrypt the packet itself Some(self.decrypt(&data)) @@ -387,16 +409,24 @@ impl SharedSecret { // Tests for std operations #[cfg(test)] mod tests { - use core::str::FromStr; - use hex::{decode_to_slice, encode, decode}; use super::*; + use core::str::FromStr; + use hex::{decode, decode_to_slice, encode}; #[test] fn public_key() { let mut slice = [0_u8; 32]; - decode_to_slice("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec", &mut slice).unwrap(); - let public_key = PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec"); - assert_eq!(Ok(PublicKey(VerifyingKey::from_bytes(&slice).unwrap())), public_key); + decode_to_slice( + "12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec", + &mut slice, + ) + .unwrap(); + let public_key = + PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec"); + assert_eq!( + Ok(PublicKey(VerifyingKey::from_bytes(&slice).unwrap())), + public_key + ); } #[test] @@ -404,7 +434,11 @@ mod tests { let private_key = PrivateKey::from_str("38DAA98490B7284697C7ADA6175FD1F8DAD12032AD7ABAE625B7EAD8FEC6444CA281C3370B97155D9C8CECD89A929FDDE0FBF3A9D5C92A1B3C24D711934CD69D").unwrap(); let public_key = PublicKey::from(&private_key); println!("Public key: {:#?}", public_key); - assert_eq!(PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap(), public_key); + assert_eq!( + PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec") + .unwrap(), + public_key + ); } #[test] @@ -416,19 +450,31 @@ mod tests { let alice_public = PublicKey::from(&alice_private); let bob_public = PublicKey::from(&bob_private); - assert_eq!(alice_public.0.as_bytes().to_vec(), decode("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap()); - assert_eq!( bob_public.0.as_bytes().to_vec(), decode("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap()); + assert_eq!( + alice_public.0.as_bytes().to_vec(), + decode("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap() + ); + assert_eq!( + bob_public.0.as_bytes().to_vec(), + decode("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap() + ); println!("Alice's public key: {}", encode(&alice_public.0.to_bytes())); println!("Bob's public key: {}", encode(&bob_public.0.to_bytes())); - let left_secret = alice_private.create_secret(&bob_public); + let left_secret = alice_private.create_secret(&bob_public); let right_secret = bob_private.create_secret(&alice_public); - assert_eq!(left_secret.0.as_bytes().to_vec(), decode("eb7a365363bd8548ee2b54b9234247be5e42e96be9625adcdf3a55b6c1d04850").unwrap()); + assert_eq!( + left_secret.0.as_bytes().to_vec(), + decode("eb7a365363bd8548ee2b54b9234247be5e42e96be9625adcdf3a55b6c1d04850").unwrap() + ); println!("Left shared secret: {}", encode(&left_secret.0.as_bytes())); - println!("Right shared secret: {}", encode(&right_secret.0.as_bytes())); + println!( + "Right shared secret: {}", + encode(&right_secret.0.as_bytes()) + ); assert_eq!(left_secret, right_secret); } @@ -436,28 +482,39 @@ mod tests { #[test] 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("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(); + let group_secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice( + &decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(), + )); + let test_group_secret = SharedSecret::from_str("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(); assert_eq!(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 - let sample_data = Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()); + let sample_data = Bytes::copy_from_slice( + &decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap(), + ); let mac = group_secret.get_hmac(&sample_data); assert_eq!(0xC3C1, mac); } #[test] fn decrypt() { - let ciphertext = Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()); - let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap())); + let ciphertext = Bytes::copy_from_slice( + &decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap(), + ); + let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice( + &decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(), + )); let cleartext = secret.decrypt(&ciphertext); println!("Cleartext: {}", encode(&cleartext)); } #[test] fn decrypt_online_example() { - let ciphertext = Bytes::copy_from_slice(&decode("9A1FD57EDFE7E4369F9FD9420C48FFAD").unwrap()); - let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("949E911CA6A6196275FF319B28C3A143").unwrap())); + let ciphertext = + Bytes::copy_from_slice(&decode("9A1FD57EDFE7E4369F9FD9420C48FFAD").unwrap()); + let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice( + &decode("949E911CA6A6196275FF319B28C3A143").unwrap(), + )); let cleartext = secret.decrypt(&ciphertext); let vec = cleartext.to_vec(); let string = String::from_utf8_lossy(&vec); @@ -467,9 +524,14 @@ mod tests { #[test] fn encrypt_online_example() { let plaintext = Bytes::copy_from_slice("Meshcore!".as_bytes()); - let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("44A6F78DAD2E54D73A32CDE3ECAA9E75").unwrap())); + let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice( + &decode("44A6F78DAD2E54D73A32CDE3ECAA9E75").unwrap(), + )); let ciphertext = secret.encrypt(plaintext); - assert_eq!(ciphertext, decode("62374852B6A11405A081F87356C88861").unwrap()); + assert_eq!( + ciphertext, + decode("62374852B6A11405A081F87356C88861").unwrap() + ); } #[test] @@ -486,11 +548,13 @@ mod tests { let padding = chunk_size - (length % chunk_size); text.reserve(padding); text.put_bytes(0, padding); - + let chunks = text.chunks_exact_mut(4); { for chunk in chunks { - for item in chunk { *item = *item << 4; } + for item in chunk { + *item = *item << 4; + } } } @@ -500,7 +564,6 @@ mod tests { println!("Result: {:#?}", encode(&text)); assert_eq!(text, decode("102030405060708090A0B0C0D0E0F0").unwrap()); - } // Example from the crate we're using @@ -508,13 +571,19 @@ mod tests { fn aes_test() { use aes::Aes128; #[allow(deprecated)] - use aes::cipher::{BlockEncrypt, BlockDecrypt, KeyInit}; + use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit}; // Initialize cipher - let key: [u8; 16] = decode("0A1BB8C05063D9941F0F1019D001B743").unwrap().try_into().unwrap(); + let key: [u8; 16] = decode("0A1BB8C05063D9941F0F1019D001B743") + .unwrap() + .try_into() + .unwrap(); let cipher = Aes128::new(&key.into()); - let message: [u8; 16] = decode("7D69AB072E09AF74EBA47EB95BF00AE3").unwrap().try_into().unwrap(); + let message: [u8; 16] = decode("7D69AB072E09AF74EBA47EB95BF00AE3") + .unwrap() + .try_into() + .unwrap(); let message_copy = message.clone(); // Encrypt block in-place @@ -533,10 +602,18 @@ mod tests { #[test] fn shared_secrets_example() { - use x25519_dalek::{StaticSecret, PublicKey}; + use x25519_dalek::{PublicKey, StaticSecret}; - let a_s: [u8; 32] = decode("58f5052c13275c8a3f4863a082555fed7ea08b9dec2eb00dd86f6b5412174458").unwrap().try_into().unwrap(); - let b_s: [u8; 32] = decode("586c4cc29635af5865abe4f231bafbd373969725493c07271e02c7fec8ff3b5f").unwrap().try_into().unwrap(); + let a_s: [u8; 32] = + decode("58f5052c13275c8a3f4863a082555fed7ea08b9dec2eb00dd86f6b5412174458") + .unwrap() + .try_into() + .unwrap(); + let b_s: [u8; 32] = + decode("586c4cc29635af5865abe4f231bafbd373969725493c07271e02c7fec8ff3b5f") + .unwrap() + .try_into() + .unwrap(); let alice_secret = StaticSecret::from(a_s); let alice_public = PublicKey::from(&alice_secret); @@ -545,26 +622,45 @@ mod tests { let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); - + assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); } #[test] fn mac_then_decrypt() { - let group_secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap())); - let sample_data = Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()); + let group_secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice( + &decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(), + )); + let sample_data = Bytes::copy_from_slice( + &decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap(), + ); let mac = 0xC3C1; let cleartext = group_secret.mac_then_decrypt(mac, &sample_data).unwrap(); - assert_eq!(cleartext, decode("3757d06800f09f8cb220547265653a20e29881efb88f00000000000000000000").unwrap()); + assert_eq!( + cleartext, + decode("3757d06800f09f8cb220547265653a20e29881efb88f00000000000000000000").unwrap() + ); } #[test] fn test_error_display() { - assert_eq!(format!("{}", MeshcoreCryptoError::KeyLengthError), "Key Length Error"); - assert_eq!(format!("{}", MeshcoreCryptoError::TryFromSliceError), "Try From Slice Error"); - assert_eq!(format!("{}", MeshcoreCryptoError::HexDecodeError), "Hex Decode Error"); - assert_eq!(format!("{}", MeshcoreCryptoError::KeyCreationError), "Key Creation Error"); + assert_eq!( + format!("{}", MeshcoreCryptoError::KeyLengthError), + "Key Length Error" + ); + assert_eq!( + format!("{}", MeshcoreCryptoError::TryFromSliceError), + "Try From Slice Error" + ); + assert_eq!( + format!("{}", MeshcoreCryptoError::HexDecodeError), + "Hex Decode Error" + ); + assert_eq!( + format!("{}", MeshcoreCryptoError::KeyCreationError), + "Key Creation Error" + ); } #[test] @@ -578,7 +674,8 @@ mod tests { let cleartext = "Hi Bob. This is alice. How are you?"; let alice_secret = alice_private.create_secret(&bob_public); - let (mac, ciphertext) = alice_secret.encrypt_then_mac(Bytes::copy_from_slice(cleartext.as_bytes())); + let (mac, ciphertext) = + alice_secret.encrypt_then_mac(Bytes::copy_from_slice(cleartext.as_bytes())); let bob_secret = bob_private.create_secret(&alice_public); let decrypted_data = bob_secret.mac_then_decrypt(mac, &ciphertext); @@ -600,6 +697,5 @@ mod tests { } else { assert!(false, "Unable to decrypt"); } - } -}
\ No newline at end of file +} @@ -19,14 +19,14 @@ pub mod std_identity; #[cfg(not(feature = "std"))] pub(crate) mod no_std_identity; +pub mod ack; +pub mod advert; +pub mod anon_req; +pub mod multipart; pub mod packet; pub mod packet_content; +pub mod path; pub mod request; pub mod response; pub mod text; -pub mod ack; -pub mod advert; -pub mod anon_req; -pub mod path; pub mod trace; -pub mod multipart;
\ No newline at end of file diff --git a/src/multipart.rs b/src/multipart.rs index ced4aaa..d9facf1 100644 --- a/src/multipart.rs +++ b/src/multipart.rs @@ -1,9 +1,8 @@ - use bytes::Bytes; #[derive(PartialEq, Debug, Clone)] pub struct MultiPart { - payload: Bytes + payload: Bytes, } impl From<Bytes> for MultiPart { @@ -21,11 +20,11 @@ impl core::fmt::Display for MultiPart { // Tests for std operations #[cfg(test)] mod tests { + use crate::{packet::*, packet_content::PacketContent}; use core::str::FromStr; use hex::decode; use tinyvec::ArrayVec; - use crate::{packet::*, packet_content::PacketContent}; - + use super::*; #[test] @@ -39,15 +38,18 @@ mod tests { transport: [0, 0], raw_content: Bytes::copy_from_slice(&decode("13E8C59624").unwrap()), content: PacketContent::Multipart(MultiPart { - payload: Bytes::copy_from_slice(&decode("13E8C59624").unwrap()) + payload: Bytes::copy_from_slice(&decode("13E8C59624").unwrap()), }), - incomplete: false + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); #[cfg(feature = "std")] - assert_eq!(format!("{}", lhs_packet), " Direct | v1 | | [] | | MULTIPART | Multipart isn't defined."); + assert_eq!( + format!("{}", lhs_packet), + " Direct | v1 | | [] | | MULTIPART | Multipart isn't defined." + ); } -}
\ No newline at end of file +} diff --git a/src/no_std_identity.rs b/src/no_std_identity.rs index 2a0018e..aac3253 100644 --- a/src/no_std_identity.rs +++ b/src/no_std_identity.rs @@ -1,5 +1,8 @@ +use crate::{ + NameString, + crypto::{Keystore, PrivateKey, PublicKey, SharedSecret}, +}; use bytes::Bytes; -use crate::{NameString, crypto::{Keystore, PrivateKey, PublicKey, SharedSecret}}; use tinyvec::ArrayVec; #[derive(PartialEq, Clone, Debug)] @@ -17,7 +20,7 @@ impl Default for Identity { Self { private_key, hash_prefix, - secrets: Default::default() + secrets: Default::default(), } } } @@ -29,7 +32,13 @@ pub struct StaticKeystore { } impl Keystore for StaticKeystore { - fn decrypt_and_id_p2p(&self, _source: u8, _dest: u8, mac: u16, data: &Bytes) -> Option<(Bytes, u32, u32)> { + fn decrypt_and_id_p2p( + &self, + _source: u8, + _dest: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, u32, u32)> { // Just brute-force all the secrets until one matches... for identity in self.identities.iter() { for (peer_prefix, secret) in identity.secrets.iter() { @@ -42,7 +51,12 @@ impl Keystore for StaticKeystore { None } - fn decrypt_and_id_group(&self, _group_hash_prefix: u8, mac: u16, data: &Bytes) -> Option<(Bytes, Option<NameString>)> { + fn decrypt_and_id_group( + &self, + _group_hash_prefix: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, Option<NameString>)> { for group in self.groups.iter() { let result = group.mac_then_decrypt(mac, data); if let Some(result) = result { @@ -53,7 +67,13 @@ impl Keystore for StaticKeystore { None } - fn decrypt_anon(&self, _dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes) -> Option<(Bytes, u32)> { + fn decrypt_anon( + &self, + _dest: u8, + pub_key: &PublicKey, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, u32)> { // For each identity, create a shared secret against the provided public key and check it. for identity in self.identities.iter() { let secret = identity.private_key.create_secret(pub_key); @@ -77,7 +97,7 @@ impl StaticKeystoreInput { pub fn compile(self) -> StaticKeystore { let mut retval = StaticKeystore { identities: ArrayVec::new(), - groups: ArrayVec::new(), + groups: ArrayVec::new(), }; for identity_in in self.identities { @@ -85,7 +105,10 @@ impl StaticKeystoreInput { identity.private_key = identity_in; identity.hash_prefix = PublicKey::from(&identity.private_key).hash_prefix(); for contact_in in self.contacts.iter() { - identity.secrets.push((contact_in.hash_prefix(), identity.private_key.create_secret(&contact_in))); + identity.secrets.push(( + contact_in.hash_prefix(), + identity.private_key.create_secret(&contact_in), + )); } retval.identities.push(identity); @@ -102,7 +125,5 @@ impl StaticKeystoreInput { #[cfg(test)] mod tests { #[test] - fn test_compile() { - - } -}
\ No newline at end of file + fn test_compile() {} +} diff --git a/src/packet.rs b/src/packet.rs index 50374a7..4cd7365 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -4,9 +4,12 @@ use crate::std_identity::Keystore; #[cfg(not(feature = "std"))] use crate::no_std_identity::Keystore; +use crate::{ + anon_req::ClearAnonRequest, packet_content::PacketContent, request::ClearRequest, + response::ClearResponse, text::ClearText, +}; use bytes::{Buf, Bytes}; use tinyvec::ArrayVec; -use crate::{anon_req::ClearAnonRequest, packet_content::PacketContent, request::ClearRequest, response::ClearResponse, text::ClearText}; #[derive(PartialEq, Clone, core::fmt::Debug)] pub struct Packet { @@ -16,7 +19,7 @@ pub struct Packet { pub transport: [u16; 2], pub raw_content: Bytes, pub content: PacketContent, - pub incomplete: bool + pub incomplete: bool, } impl core::str::FromStr for Packet { @@ -36,7 +39,7 @@ impl From<Bytes> for Packet { let mut packet = Packet::default(); if bytes.len() < 2 { - return packet + return packet; } let header = bytes.get_u8(); @@ -49,25 +52,29 @@ impl From<Bytes> for Packet { packet.transport = match packet.route_type { RouteType::TransportFlood | RouteType::TransportDirect => { // The packet isn't long enough to contain the transport - if bytes.len() < 4 { return packet; } + if bytes.len() < 4 { + return packet; + } let t1 = bytes.get_u16_le(); let t2 = bytes.get_u16_le(); [t1, t2] - }, - _ => { - [0, 0] } + _ => [0, 0], }; // Get the route - if bytes.is_empty() { return packet; } + if bytes.is_empty() { + return packet; + } let path_length = bytes.get_u8() as usize; packet.path = match packet.version { PayloadVersion::VersionOne => { // The packet isn't long enough for the indicated route - if bytes.len() < path_length { return packet; } + if bytes.len() < path_length { + return packet; + } let mut route = ArrayVec::new(); for _ in 0..path_length { @@ -76,11 +83,13 @@ impl From<Bytes> for Packet { } route - }, + } PayloadVersion::VersionTwo => { // The packet isn't long enough for the indicated route - if bytes.len() < path_length * 2 { return packet; } + if bytes.len() < path_length * 2 { + return packet; + } let mut route = ArrayVec::new(); for i in 0..path_length { @@ -89,7 +98,7 @@ impl From<Bytes> for Packet { } route - }, + } _ => { return packet; } @@ -121,7 +130,7 @@ impl From<Bytes> for Packet { // when it prints the SNR results in its Display function. packet.content = PacketContent::Trace(trace); } - + // Mark the packet as complete and valid packet.incomplete = false; @@ -138,7 +147,7 @@ impl Default for Packet { transport: [0, 0], raw_content: Bytes::new(), content: PacketContent::Invalid, - incomplete: true + incomplete: true, } } } @@ -149,8 +158,13 @@ impl core::fmt::Display for Packet { f.write_str(" | ")?; core::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]))? + 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!(" | |"))? } @@ -159,9 +173,21 @@ impl core::fmt::Display for Packet { match self.path.len() { 0 => f.write_fmt(format_args!(" [] | ")), 1 => f.write_fmt(format_args!(" [{:02x?}] | ", self.path[0])), - 2 => f.write_fmt(format_args!(" [{:02x?}, {:02x?}] | ", self.path[0], self.path[1])), - 3 => f.write_fmt(format_args!(" [{:02x?}, {:02x?}, {:02x?}] | ", self.path[0], self.path[1], self.path[2])), - _ => f.write_fmt(format_args!(" [{:02x?}, {:02x?}, ... {:02x?}, {:02x?}] | ", self.path[0], self.path[1], self.path[len - 2], self.path[len -1])), + 2 => f.write_fmt(format_args!( + " [{:02x?}, {:02x?}] | ", + self.path[0], self.path[1] + )), + 3 => f.write_fmt(format_args!( + " [{:02x?}, {:02x?}, {:02x?}] | ", + self.path[0], self.path[1], self.path[2] + )), + _ => f.write_fmt(format_args!( + " [{:02x?}, {:02x?}, ... {:02x?}, {:02x?}] | ", + self.path[0], + self.path[1], + self.path[len - 2], + self.path[len - 1] + )), }?; if self.incomplete { @@ -179,21 +205,19 @@ impl Packet { pub fn try_decrypt(&mut self, keystore: &Keystore) -> bool { match self.content { // Encrypted packet types - PacketContent::Path(ref mut path) => { - path.cipher.try_decrypt(keystore) - }, + PacketContent::Path(ref mut path) => path.cipher.try_decrypt(keystore), PacketContent::Request(ref mut request) => { let result = request.cipher.try_decrypt(keystore); if let Some(cleartext) = &request.cipher.cleartext { let cleartext = ClearRequest::from(cleartext.clone()); request.cleartext = Some(cleartext); - + result } else { false } - }, + } PacketContent::Response(ref mut response) => { response.cipher.try_decrypt(keystore); @@ -203,7 +227,7 @@ impl Packet { } else { false } - }, + } PacketContent::Text(ref mut text) => { text.cipher.try_decrypt(keystore); @@ -214,14 +238,14 @@ impl Packet { } else { false } - }, + } PacketContent::AnonReq(ref mut anon_req) => { let decrypt_result = keystore.decrypt_anon( anon_req.dest, &anon_req.public_key, anon_req.mac, - &anon_req.ciphertext + &anon_req.ciphertext, ); if let Some(cleartext) = decrypt_result { @@ -232,14 +256,12 @@ impl Packet { } } - PacketContent::GroupText(ref mut group_text) => { - group_text.try_decrypt(keystore) - } + PacketContent::GroupText(ref mut group_text) => group_text.try_decrypt(keystore), // None of the other packets implement any encryption - _ => false + _ => false, } - } + } } #[derive(PartialEq, Debug, Clone)] @@ -254,11 +276,11 @@ pub enum RouteType { impl core::fmt::Display for RouteType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - RouteType::TransportFlood => f.write_str("T-Flood "), - RouteType::Flood => f.write_str(" Flood "), - RouteType::Direct => f.write_str(" Direct"), + 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 "), + RouteType::Invalid => f.write_str("INVALID "), } } } @@ -270,7 +292,7 @@ impl From<u8> for RouteType { 0x01 => RouteType::Flood, 0x02 => RouteType::Direct, 0x03 => RouteType::TransportDirect, - _ => RouteType::Invalid + _ => RouteType::Invalid, } } } @@ -294,7 +316,7 @@ impl From<u8> for PayloadVersion { 0x01 => PayloadVersion::VersionTwo, 0x02 => PayloadVersion::VersionThree, 0x03 => PayloadVersion::VersionFour, - _ => PayloadVersion::Invalid + _ => PayloadVersion::Invalid, } } } @@ -302,11 +324,11 @@ impl From<u8> for PayloadVersion { impl core::fmt::Display for PayloadVersion { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - PayloadVersion::VersionOne => f.write_str("v1"), - PayloadVersion::VersionTwo => f.write_str("v2"), + 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"), + PayloadVersion::VersionFour => f.write_str("v4"), + PayloadVersion::Invalid => f.write_str("xx"), } } } @@ -316,50 +338,53 @@ mod tests { use core::str::FromStr; use tinyvec::array_vec; - use crate::packet_content::{PacketContent, Raw}; use super::*; + use crate::packet_content::{PacketContent, Raw}; #[test] fn header_route_type() { // The route type is the lowest-order two bits - assert_eq!(RouteType::TransportFlood, RouteType::from(0x00)); - assert_eq!(RouteType::TransportFlood, RouteType::from(0xFC)); - assert_eq!(RouteType::Flood, RouteType::from(0x01)); - assert_eq!(RouteType::Flood, RouteType::from(0xFD)); - assert_eq!(RouteType::Direct, RouteType::from(0x02)); - assert_eq!(RouteType::Direct, RouteType::from(0xFE)); + assert_eq!(RouteType::TransportFlood, RouteType::from(0x00)); + assert_eq!(RouteType::TransportFlood, RouteType::from(0xFC)); + assert_eq!(RouteType::Flood, RouteType::from(0x01)); + assert_eq!(RouteType::Flood, RouteType::from(0xFD)); + assert_eq!(RouteType::Direct, RouteType::from(0x02)); + assert_eq!(RouteType::Direct, RouteType::from(0xFE)); assert_eq!(RouteType::TransportDirect, RouteType::from(0x03)); assert_eq!(RouteType::TransportDirect, RouteType::from(0xFF)); - assert_eq!(format!("{}", RouteType::TransportFlood), "T-Flood "); - assert_eq!(format!("{}", RouteType::Flood), " Flood "); - assert_eq!(format!("{}", RouteType::Direct), " Direct"); + assert_eq!(format!("{}", RouteType::TransportFlood), "T-Flood "); + assert_eq!(format!("{}", RouteType::Flood), " Flood "); + assert_eq!(format!("{}", RouteType::Direct), " Direct"); assert_eq!(format!("{}", RouteType::TransportDirect), "T-Direct"); - assert_eq!(format!("{}", RouteType::Invalid), "INVALID "); + assert_eq!(format!("{}", RouteType::Invalid), "INVALID "); } #[test] fn header_version() { - assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x00)); - assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x3F)); - assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x40)); - assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x7F)); + assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x00)); + assert_eq!(PayloadVersion::VersionOne, PayloadVersion::from(0x3F)); + assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x40)); + assert_eq!(PayloadVersion::VersionTwo, PayloadVersion::from(0x7F)); assert_eq!(PayloadVersion::VersionThree, PayloadVersion::from(0x80)); assert_eq!(PayloadVersion::VersionThree, PayloadVersion::from(0xBF)); - assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xC0)); - assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xFF)); + assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xC0)); + assert_eq!(PayloadVersion::VersionFour, PayloadVersion::from(0xFF)); - assert_eq!(format!("{}", PayloadVersion::VersionOne), "v1"); - assert_eq!(format!("{}", PayloadVersion::VersionTwo), "v2"); + assert_eq!(format!("{}", PayloadVersion::VersionOne), "v1"); + assert_eq!(format!("{}", PayloadVersion::VersionTwo), "v2"); assert_eq!(format!("{}", PayloadVersion::VersionThree), "v3"); - assert_eq!(format!("{}", PayloadVersion::VersionFour), "v4"); - assert_eq!(format!("{}", PayloadVersion::Invalid), "xx"); + assert_eq!(format!("{}", PayloadVersion::VersionFour), "v4"); + assert_eq!(format!("{}", PayloadVersion::Invalid), "xx"); } #[test] fn packet() { // Check the hex decode errors - assert_eq!(Err(hex::FromHexError::InvalidHexCharacter { c: 's', index: 0 }), Packet::from_str("s0")); + assert_eq!( + Err(hex::FromHexError::InvalidHexCharacter { c: 's', index: 0 }), + Packet::from_str("s0") + ); assert_eq!(Err(hex::FromHexError::OddLength), Packet::from_str("0")); // Check errors related to packet length issues @@ -387,7 +412,7 @@ mod tests { lhs_packet.version = PayloadVersion::VersionTwo; let rhs_packet = Packet::from_str("420102").unwrap(); assert_eq!(lhs_packet, rhs_packet); - + // Ensure packet remainder is captured. This // will test version 1 plus transport let lhs_packet = Packet { @@ -395,12 +420,19 @@ mod tests { version: PayloadVersion::VersionOne, path: array_vec!([u16; 64] => 0x06, 0x07, 0x08, 0x09, 0x0A), transport: [0x0102, 0x0304], - raw_content: Bytes::copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19]), - content: PacketContent::Raw(Raw { bytes: Bytes::copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19])}), + raw_content: Bytes::copy_from_slice(&[ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + ]), + content: PacketContent::Raw(Raw { + bytes: Bytes::copy_from_slice(&[ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + ]), + }), incomplete: false, }; - let rhs_packet: Packet = Packet::from_str("3F0201040305060708090A10111213141516171819").unwrap(); + let rhs_packet: Packet = + Packet::from_str("3F0201040305060708090A10111213141516171819").unwrap(); let compare_string = "T-Direct | v1 | 102, 304 | [06, 07, ... 09, 0a] | | RAW | Raw { bytes: b\"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\" }"; assert_eq!(format!("{}", rhs_packet), compare_string); @@ -417,21 +449,36 @@ mod tests { transport: [0, 1], raw_content: Bytes::new(), content: PacketContent::Invalid, - incomplete: false + incomplete: false, }; - assert_eq!(format!("{}", packet), " Direct | v1 | | [] | | INVALID | INVALID"); + assert_eq!( + format!("{}", packet), + " Direct | v1 | | [] | | INVALID | INVALID" + ); packet.path.push(0x01); - assert_eq!(format!("{}", packet), " Direct | v1 | | [01] | | INVALID | INVALID"); + assert_eq!( + format!("{}", packet), + " Direct | v1 | | [01] | | INVALID | INVALID" + ); packet.path.push(0x02); - assert_eq!(format!("{}", packet), " Direct | v1 | | [01, 02] | | INVALID | INVALID"); + assert_eq!( + format!("{}", packet), + " Direct | v1 | | [01, 02] | | INVALID | INVALID" + ); packet.path.push(0xff); - assert_eq!(format!("{}", packet), " Direct | v1 | | [01, 02, ff] | | INVALID | INVALID"); + assert_eq!( + format!("{}", packet), + " Direct | v1 | | [01, 02, ff] | | INVALID | INVALID" + ); packet.incomplete = true; - assert_eq!(format!("{}", packet), " Direct | v1 | | [01, 02, ff] | x | INVALID | INVALID"); + assert_eq!( + format!("{}", packet), + " Direct | v1 | | [01, 02, ff] | x | INVALID | INVALID" + ); } -}
\ No newline at end of file +} diff --git a/src/packet_content.rs b/src/packet_content.rs index 26230e1..a6fa914 100644 --- a/src/packet_content.rs +++ b/src/packet_content.rs @@ -1,5 +1,15 @@ +use crate::{ + ack::Ack, + advert::Advert, + anon_req::AnonReq, + multipart::MultiPart, + path::Path, + request::Request, + response::Response, + text::{GroupData, GroupText, Text}, + trace::Trace, +}; use bytes::{Buf, Bytes}; -use crate::{ack::Ack, advert::Advert, anon_req::AnonReq, multipart::MultiPart, path::Path, request::Request, response::Response, text::{GroupData, GroupText, Text}, trace::Trace}; #[cfg(feature = "std")] use crate::std_identity::Keystore; @@ -23,41 +33,41 @@ pub enum PacketContent { } impl PacketContent { - fn justified_name(&self) -> &str{ + fn justified_name(&self) -> &str { match self { - PacketContent::Request(_) => " REQUEST | ", - PacketContent::Response(_) => " RESPONSE | ", - PacketContent::Text(_) => " TEXT | ", - PacketContent::Ack(_) => " ACK | ", - PacketContent::Advert(_) => " ADVERT | ", + 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::AnonReq(_) => " ANON REQ. | ", + PacketContent::Path(_) => " PATH | ", + PacketContent::Trace(_) => " TRACE | ", PacketContent::Multipart(_) => " MULTIPART | ", - PacketContent::Raw(_) => " RAW | ", - PacketContent::Invalid => " INVALID | ", + 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 { - 0x00 => PacketContent::Request( Request::from(bytes)), - 0x01 => PacketContent::Response( Response::from(bytes)), - 0x02 => PacketContent::Text( Text::from(bytes)), - 0x03 => PacketContent::Ack( Ack::from(bytes)), - 0x04 => PacketContent::Advert( Advert::from(bytes)), + 0x00 => PacketContent::Request(Request::from(bytes)), + 0x01 => PacketContent::Response(Response::from(bytes)), + 0x02 => PacketContent::Text(Text::from(bytes)), + 0x03 => PacketContent::Ack(Ack::from(bytes)), + 0x04 => PacketContent::Advert(Advert::from(bytes)), 0x05 => PacketContent::GroupText(GroupText::from(bytes)), 0x06 => PacketContent::GroupData(GroupData::from(bytes)), - 0x07 => PacketContent::AnonReq( AnonReq::from(bytes)), - 0x08 => PacketContent::Path( Path::from(bytes)), - 0x09 => PacketContent::Trace( Trace::from(bytes)), + 0x07 => PacketContent::AnonReq(AnonReq::from(bytes)), + 0x08 => PacketContent::Path(Path::from(bytes)), + 0x09 => PacketContent::Trace(Trace::from(bytes)), 0x0A => PacketContent::Multipart(MultiPart::from(bytes)), - 0x0F => PacketContent::Raw( Raw { bytes }), + 0x0F => PacketContent::Raw(Raw { bytes }), - _ => PacketContent::Invalid + _ => PacketContent::Invalid, } } } @@ -67,19 +77,19 @@ impl core::fmt::Display for PacketContent { f.write_str(self.justified_name())?; match self { - PacketContent::Request(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Response(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Text(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Ack(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Advert(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Request(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Response(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Text(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Ack(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Advert(c) => core::fmt::Display::fmt(&c, f), PacketContent::GroupText(c) => core::fmt::Display::fmt(&c, f), PacketContent::GroupData(c) => core::fmt::Display::fmt(&c, f), - PacketContent::AnonReq(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Path(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Trace(c) => core::fmt::Display::fmt(&c, f), + PacketContent::AnonReq(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Path(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Trace(c) => core::fmt::Display::fmt(&c, f), PacketContent::Multipart(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Raw(c) => core::fmt::Display::fmt(&c, f), - PacketContent::Invalid => f.write_str("INVALID") + PacketContent::Raw(c) => core::fmt::Display::fmt(&c, f), + PacketContent::Invalid => f.write_str("INVALID"), } } } @@ -90,7 +100,7 @@ pub enum NodeType { Room, Repeater, Sensor, - Invalid + Invalid, } impl From<u8> for NodeType { @@ -101,7 +111,7 @@ impl From<u8> for NodeType { 0x02 => NodeType::Repeater, 0x03 => NodeType::Room, 0x04 => NodeType::Sensor, - _ => NodeType::Invalid + _ => NodeType::Invalid, } } } @@ -112,7 +122,7 @@ pub struct PeerToPeerCipher { pub source: u8, pub mac: u16, pub ciphertext: Bytes, - pub cleartext: Option<Bytes> + pub cleartext: Option<Bytes>, } impl From<Bytes> for PeerToPeerCipher { @@ -124,11 +134,13 @@ impl From<Bytes> for PeerToPeerCipher { source: 0x00, mac: 0x0000, ciphertext: Bytes::new(), - cleartext: None + cleartext: None, }; // Just check for the whole fixed-size part at once - if bytes.len() < 4 { return response } + if bytes.len() < 4 { + return response; + } response.destination = bytes.get_u8(); response.source = bytes.get_u8(); response.mac = bytes.get_u16(); @@ -141,14 +153,10 @@ impl From<Bytes> for PeerToPeerCipher { impl PeerToPeerCipher { #[cfg(feature = "std")] - pub fn try_decrypt(&mut self, keystore: &Keystore) -> bool { - let decrypt = keystore.decrypt_and_id_p2p( - self.source, - self.destination, - self.mac, - &self.ciphertext - ); - + 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 @@ -160,7 +168,7 @@ impl PeerToPeerCipher { #[derive(PartialEq, Debug, Clone)] pub struct Raw { - pub(crate) bytes: Bytes + pub(crate) bytes: Bytes, } impl core::fmt::Display for Raw { @@ -176,16 +184,16 @@ mod tests { #[test] fn node_type() { - assert_eq!(NodeType::Invalid, NodeType::from(0x00)); - assert_eq!(NodeType::Invalid, NodeType::from(0xF8)); - assert_eq!(NodeType::Chat, NodeType::from(0x01)); - assert_eq!(NodeType::Chat, NodeType::from(0xF9)); + assert_eq!(NodeType::Invalid, NodeType::from(0x00)); + assert_eq!(NodeType::Invalid, NodeType::from(0xF8)); + assert_eq!(NodeType::Chat, NodeType::from(0x01)); + assert_eq!(NodeType::Chat, NodeType::from(0xF9)); assert_eq!(NodeType::Repeater, NodeType::from(0x02)); assert_eq!(NodeType::Repeater, NodeType::from(0xFA)); - assert_eq!(NodeType::Room, NodeType::from(0x03)); - assert_eq!(NodeType::Room, NodeType::from(0xFB)); - assert_eq!(NodeType::Sensor, NodeType::from(0x04)); - assert_eq!(NodeType::Sensor, NodeType::from(0xFC)); - assert_eq!(NodeType::Invalid, NodeType::from(0x05)); + assert_eq!(NodeType::Room, NodeType::from(0x03)); + assert_eq!(NodeType::Room, NodeType::from(0xFB)); + assert_eq!(NodeType::Sensor, NodeType::from(0x04)); + assert_eq!(NodeType::Sensor, NodeType::from(0xFC)); + assert_eq!(NodeType::Invalid, NodeType::from(0x05)); } -}
\ No newline at end of file +} diff --git a/src/path.rs b/src/path.rs index a2eb1c9..a1aaedd 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,5 +1,5 @@ -use bytes::Bytes; use crate::packet_content::PeerToPeerCipher; +use bytes::Bytes; #[derive(PartialEq, Debug, Clone)] pub struct Path { @@ -9,25 +9,31 @@ pub struct Path { impl From<Bytes> for Path { fn from(value: Bytes) -> Self { Path { - cipher: PeerToPeerCipher::from(value) + cipher: PeerToPeerCipher::from(value), } } } impl core::fmt::Display for Path { 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 + )) } } // Tests for std operations #[cfg(test)] mod tests { + use crate::{ + packet::*, + packet_content::{PacketContent, PeerToPeerCipher}, + }; use core::str::FromStr; use hex::decode; use tinyvec::array_vec; - use crate::{packet::*, packet_content::{PacketContent, PeerToPeerCipher}}; - + use super::*; #[test] @@ -39,23 +45,32 @@ mod tests { version: PayloadVersion::VersionOne, path: array_vec!([u16; 64] => 0xBA, 0x03, 0x12, 0x7F, 0x7E, 0xA9, 0x22), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("1351768DD2DF32E1D02F5851379F5AFCC667AB273442FAB2943673F26DDBEB9595027474").unwrap()), + raw_content: Bytes::copy_from_slice( + &decode("1351768DD2DF32E1D02F5851379F5AFCC667AB273442FAB2943673F26DDBEB9595027474") + .unwrap(), + ), content: PacketContent::Path(Path { cipher: PeerToPeerCipher { destination: 0x13, source: 0x51, mac: 0x768D, - ciphertext: Bytes::copy_from_slice(&decode("D2DF32E1D02F5851379F5AFCC667AB273442FAB2943673F26DDBEB9595027474").unwrap()), - cleartext: None - } + ciphertext: Bytes::copy_from_slice( + &decode("D2DF32E1D02F5851379F5AFCC667AB273442FAB2943673F26DDBEB9595027474") + .unwrap(), + ), + cleartext: None, + }, }), - incomplete: false + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); #[cfg(feature = "std")] - assert_eq!(format!("{}", lhs_packet), " Flood | v1 | | [ba, 03, ... a9, 22] | | PATH | (51) -> (13) MAC: 768d "); + assert_eq!( + format!("{}", lhs_packet), + " Flood | v1 | | [ba, 03, ... a9, 22] | | PATH | (51) -> (13) MAC: 768d " + ); } -}
\ No newline at end of file +} diff --git a/src/request.rs b/src/request.rs index fcf6278..0880c41 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,10 +1,10 @@ -use chrono::{DateTime, Utc}; use crate::packet_content::PeerToPeerCipher; use bytes::{Buf, Bytes}; +use chrono::{DateTime, Utc}; #[derive(PartialEq, Debug, Clone)] pub struct Request { - pub cipher: PeerToPeerCipher, + pub cipher: PeerToPeerCipher, pub cleartext: Option<ClearRequest>, } @@ -12,7 +12,7 @@ pub struct Request { pub struct ClearRequest { pub timestamp: DateTime<Utc>, pub request_type: RequestType, - pub request_data: Bytes + pub request_data: Bytes, } impl From<Bytes> for ClearRequest { @@ -22,11 +22,13 @@ impl From<Bytes> for ClearRequest { let mut clear_request = ClearRequest { timestamp: DateTime::from_timestamp(0, 0).unwrap(), request_type: RequestType::Invalid, - request_data: Bytes::new() + request_data: Bytes::new(), }; // Just check for the whole fixed-size part at once - if bytes.len() < 5 { return clear_request } + if bytes.len() < 5 { + return clear_request; + } if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32() as i64, 0) { clear_request.timestamp = timestamp; } @@ -59,7 +61,7 @@ impl From<u8> for RequestType { 0x04 => RequestType::MinMaxAvg, 0x05 => RequestType::ACL, 0x06 => RequestType::Neighbors, - _ => RequestType::Invalid + _ => RequestType::Invalid, } } } @@ -67,13 +69,13 @@ impl From<u8> for RequestType { impl core::fmt::Display for RequestType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - RequestType::Stats => f.write_str("STATS"), + RequestType::Stats => f.write_str("STATS"), RequestType::Keepalive => f.write_str("KEEP ALIVE"), RequestType::Telemetry => f.write_str("TELEMETRY"), RequestType::MinMaxAvg => f.write_str("MIN/MAX/AVG"), - RequestType::ACL => f.write_str("ACL"), + RequestType::ACL => f.write_str("ACL"), RequestType::Neighbors => f.write_str("NEIGHBORS"), - RequestType::Invalid => f.write_str("INVALID"), + RequestType::Invalid => f.write_str("INVALID"), } } } @@ -82,17 +84,16 @@ impl From<Bytes> for Request { fn from(value: Bytes) -> Self { Request { cipher: PeerToPeerCipher::from(value), - cleartext: None + cleartext: None, } } } impl core::fmt::Display for Request { 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 { @@ -108,11 +109,16 @@ impl core::fmt::Display for Request { mod tests { use std::str::FromStr; + use crate::{ + packet::*, + packet_content::{PacketContent, PeerToPeerCipher}, + request::{ClearRequest, Request, RequestType}, + std_identity::KeystoreInput, + }; + use bytes::Bytes; use chrono::DateTime; use hex::decode; - use bytes::Bytes; use tinyvec::ArrayVec; - use crate::{std_identity::KeystoreInput, packet::*, packet_content::{PacketContent, PeerToPeerCipher}, request::{ClearRequest, Request, RequestType}}; #[test] fn request_type() { @@ -125,14 +131,13 @@ mod tests { assert_eq!(RequestType::from(0x07), RequestType::Invalid); assert_eq!(RequestType::from(0xFF), RequestType::Invalid); - - assert_eq!(format!("{}", RequestType::Stats), "STATS"); + assert_eq!(format!("{}", RequestType::Stats), "STATS"); assert_eq!(format!("{}", RequestType::Keepalive), "KEEP ALIVE"); assert_eq!(format!("{}", RequestType::Telemetry), "TELEMETRY"); assert_eq!(format!("{}", RequestType::MinMaxAvg), "MIN/MAX/AVG"); - assert_eq!(format!("{}", RequestType::ACL), "ACL"); + assert_eq!(format!("{}", RequestType::ACL), "ACL"); assert_eq!(format!("{}", RequestType::Neighbors), "NEIGHBORS"); - assert_eq!(format!("{}", RequestType::Invalid), "INVALID"); + assert_eq!(format!("{}", RequestType::Invalid), "INVALID"); } #[test] @@ -144,20 +149,26 @@ mod tests { version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("12341d87ccaac89563cbb39d2333b725e407a1a6").unwrap()), + 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()), - cleartext: Some(Bytes::copy_from_slice(b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0")), + ciphertext: Bytes::copy_from_slice( + &decode("ccaac89563cbb39d2333b725e407a1a6").unwrap(), + ), + cleartext: Some(Bytes::copy_from_slice( + b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0", + )), }, cleartext: Some(ClearRequest { timestamp: DateTime::from_timestamp_secs(3524712861).unwrap(), request_type: crate::request::RequestType::Stats, request_data: Bytes::copy_from_slice(b"\0\0\0\0\x9d0\x96\xbb\0\0\0"), - }) + }), }), incomplete: false, }; @@ -167,7 +178,10 @@ mod tests { let file_contents = include_str!("../test_identities_file.toml"); let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap(); let keystore = keystore_in.compile(); - assert_eq!(format!("{}", rhs_packet.content), " REQUEST | (34) -> (12) MAC: 1d87 ENCRYPTED"); + assert_eq!( + format!("{}", rhs_packet.content), + " REQUEST | (34) -> (12) MAC: 1d87 ENCRYPTED" + ); rhs_packet.try_decrypt(&keystore); @@ -179,26 +193,32 @@ mod tests { #[test] fn neighbors_request() { let sample = "02001234EB5862E311F3321EB6EE9BEB75E060342CF8"; - - let lhs_packet = Packet { + + let lhs_packet = Packet { route_type: RouteType::Direct, version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], - raw_content: Bytes::copy_from_slice(&decode("1234EB5862E311F3321EB6EE9BEB75E060342CF8").unwrap()), + raw_content: Bytes::copy_from_slice( + &decode("1234EB5862E311F3321EB6EE9BEB75E060342CF8").unwrap(), + ), content: PacketContent::Request(Request { cipher: PeerToPeerCipher { destination: 0x12, source: 0x34, mac: 0xEB58, - ciphertext: Bytes::copy_from_slice(&decode("62E311F3321EB6EE9BEB75E060342CF8").unwrap()), - cleartext: Some(Bytes::copy_from_slice(b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0")), + ciphertext: Bytes::copy_from_slice( + &decode("62E311F3321EB6EE9BEB75E060342CF8").unwrap(), + ), + cleartext: Some(Bytes::copy_from_slice( + b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0", + )), }, cleartext: Some(ClearRequest { timestamp: DateTime::from_timestamp_secs(3524712861).unwrap(), request_type: crate::request::RequestType::Neighbors, - request_data: Bytes::copy_from_slice( b"\0\n\0\0\0\x04\x07v\x95\xb1\0"), - }) + request_data: Bytes::copy_from_slice(b"\0\n\0\0\0\x04\x07v\x95\xb1\0"), + }), }), incomplete: false, }; @@ -216,6 +236,5 @@ mod tests { // assert!(format!("{}", rhs_packet.content) == " REQUEST | (34) -> (12) MAC: 1d87 at: 2081-09-10 06:54:21 UTC STATS"); // assert_eq!(lhs_packet, rhs_packet); - } - -}
\ No newline at end of file + } +} diff --git a/src/response.rs b/src/response.rs index f68e881..9e95c19 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,19 +1,18 @@ - +use crate::packet_content::PeerToPeerCipher; use bytes::{Buf, Bytes}; use chrono::{DateTime, Utc}; -use crate::packet_content::PeerToPeerCipher; #[derive(PartialEq, Debug, Clone)] pub struct Response { pub(crate) cipher: PeerToPeerCipher, - pub cleartext: Option<ClearResponse> + pub cleartext: Option<ClearResponse>, } impl From<Bytes> for Response { fn from(value: Bytes) -> Self { Response { cipher: PeerToPeerCipher::from(value), - cleartext: None + cleartext: None, } } } @@ -34,29 +33,27 @@ pub enum ResponseContent { #[repr(C)] #[derive(PartialEq, Debug, Clone)] pub struct RepeaterStats { - pub batt_milli_volts: u16, - pub curr_tx_queue_len: u16, - pub noise_floor: i16, - pub last_rssi: i16, - pub n_packets_recv: u32, - pub n_packets_sent: u32, - pub total_air_time_secs: u32, - pub total_up_time_secs: u32, - pub n_sent_flood: u32, - pub n_sent_direct: u32, - pub n_recv_flood: u32, - pub n_recv_direct: u32, - pub err_events: u16, - pub last_snr: i16, - pub n_direct_dups: u16, - pub n_flood_dups: u16, - pub total_rx_air_time_secs: u32, + pub batt_milli_volts: u16, + pub curr_tx_queue_len: u16, + pub noise_floor: i16, + pub last_rssi: i16, + pub n_packets_recv: u32, + pub n_packets_sent: u32, + pub total_air_time_secs: u32, + pub total_up_time_secs: u32, + pub n_sent_flood: u32, + pub n_sent_direct: u32, + pub n_recv_flood: u32, + pub n_recv_direct: u32, + pub err_events: u16, + pub last_snr: i16, + pub n_direct_dups: u16, + pub n_flood_dups: u16, + pub total_rx_air_time_secs: u32, } #[derive(PartialEq, Debug, Clone)] -pub struct Telemetry { - -} +pub struct Telemetry {} impl From<Bytes> for ClearResponse { fn from(value: Bytes) -> Self { @@ -64,10 +61,12 @@ impl From<Bytes> for ClearResponse { let mut clear_response = ClearResponse { timestamp: DateTime::from_timestamp(0, 0).unwrap(), - content: ResponseContent::Invalid + content: ResponseContent::Invalid, }; - if bytes.len() < 4 { return clear_response } + if bytes.len() < 4 { + return clear_response; + } if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32_le() as i64, 0) { clear_response.timestamp = timestamp; } @@ -78,9 +77,9 @@ impl From<Bytes> for ClearResponse { // are exactly 0x0174 then we'll assume it's a telemetry packet. // let maybe_cayenne = bytes.clone().get_u16(); // if maybe_cayenne == 0x0174 { - // println!("CayenneLPP: {} bytes: {}", bytes.len(), encode(&bytes)); + // println!("CayenneLPP: {} bytes: {}", bytes.len(), encode(&bytes)); // } else { - // println!("{} bytes: {}", value.len(), encode(&value)); + // println!("{} bytes: {}", value.len(), encode(&value)); // } clear_response @@ -89,10 +88,9 @@ impl From<Bytes> for ClearResponse { impl core::fmt::Display for Response { 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.cipher.cleartext { @@ -105,11 +103,16 @@ impl core::fmt::Display for Response { #[cfg(test)] mod tests { - use std::str::FromStr; + use crate::{ + packet::*, + packet_content::{PacketContent, PeerToPeerCipher}, + response::Response, + std_identity::KeystoreInput, + }; use bytes::Bytes; use hex::decode; + use std::str::FromStr; use tinyvec::ArrayVec; - use crate::{std_identity::KeystoreInput, packet::*, packet_content::{PacketContent, PeerToPeerCipher}, response::Response}; #[test] fn response_encrypted() { @@ -136,7 +139,10 @@ mod tests { let rhs_packet = Packet::from_str(sample).unwrap(); assert_eq!(lhs_packet, rhs_packet); - assert_eq!(format!("{}", lhs_packet), " Direct | v1 | | [] | | RESPONSE | (12) -> (34) MAC: b900 ENCRYPTED") + assert_eq!( + format!("{}", lhs_packet), + " Direct | v1 | | [] | | RESPONSE | (12) -> (34) MAC: b900 ENCRYPTED" + ) } #[test] @@ -269,7 +275,6 @@ mod tests { // assert_eq!(lhs_packet, rhs_packet); } - #[test] fn telemetry_response() { let sample = "06003412187C3AE03E52D347B957D634221DB5E86815"; @@ -301,4 +306,4 @@ mod tests { println!("{:#?}", rhs_packet); // assert_eq!(lhs_packet, rhs_packet); } -}
\ No newline at end of file +} diff --git a/src/std_identity.rs b/src/std_identity.rs index fdc8dff..80c1651 100644 --- a/src/std_identity.rs +++ b/src/std_identity.rs @@ -1,14 +1,18 @@ -use std::{collections::{HashMap, HashSet}, rc::Rc, str::FromStr}; +use crate::crypto::{PrivateKey, PublicKey, SharedSecret}; use bytes::Bytes; use log::warn; -use crate::{crypto::{PrivateKey, PublicKey, SharedSecret}}; +use std::{ + collections::{HashMap, HashSet}, + rc::Rc, + str::FromStr, +}; use serde::{Deserialize, de}; #[derive(PartialEq, Clone, Deserialize, Debug)] /// The Identity structure contains the information to decrypt /// incoming messages and sign and encrypt outgoing messages. -/// +/// /// It has the necessary cryptographic material needed to appear /// as this identity when creating messages. pub struct Identity { @@ -34,14 +38,14 @@ pub struct Identity { /// used because there are many many hash collisions /// when using just the 1-byte hash prefix. #[serde(skip)] - pub secrets: HashSet<(Rc<String>, SharedSecret)> + pub secrets: HashSet<(Rc<String>, SharedSecret)>, } #[derive(PartialEq, Clone, Deserialize, Debug)] /// The Contact structure contains the information needed /// to decrypt messages from a remote user intended for /// either an identity or a channel. -/// +/// /// It's cryptographic material is also needed to uniquely /// identify the origin of a message. Most source values /// are only one byte, so that information just limits the @@ -53,7 +57,7 @@ pub struct Contact { /// The provided public key of the remote contact #[serde(deserialize_with = "deserialize_public_key")] - pub public_key: PublicKey + pub public_key: PublicKey, } #[derive(PartialEq, Clone, Deserialize, Debug)] @@ -68,7 +72,7 @@ pub struct Group { /// The group's shared secret #[serde(deserialize_with = "deserialize_secret")] - pub secret: SharedSecret + pub secret: SharedSecret, } #[derive(PartialEq, Clone, Deserialize, Debug)] @@ -87,7 +91,13 @@ impl Keystore { } } - pub fn decrypt_and_id_p2p(&self, _source: u8, _dest: u8, mac: u16, data: &Bytes) -> Option<(Bytes, Rc<String>, Rc<String>)> { + pub fn decrypt_and_id_p2p( + &self, + _source: u8, + _dest: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, Rc<String>, Rc<String>)> { // Just brute-force all the secrets until one matches... for identity in self.identities.iter() { for secret in identity.1.secrets.iter() { @@ -100,7 +110,12 @@ impl Keystore { None } - pub fn decrypt_and_id_group(&self, _group_hash_prefix: u8, mac: u16, data: &Bytes) -> Option<(Bytes, u32)> { + pub fn decrypt_and_id_group( + &self, + _group_hash_prefix: u8, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, u32)> { for group in self.groups.iter() { let result = group.1.secret.mac_then_decrypt(mac, data); if let Some(result) = result { @@ -111,7 +126,13 @@ impl Keystore { None } - pub fn decrypt_anon(&self, _dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes) -> Option<(Bytes, Rc<String>)> { + pub fn decrypt_anon( + &self, + _dest: u8, + pub_key: &PublicKey, + mac: u16, + data: &Bytes, + ) -> Option<(Bytes, Rc<String>)> { // For each identity, create a shared secret against the provided public key and check it. for identity in self.identities.iter() { let secret = identity.1.private_key.create_secret(pub_key); @@ -136,7 +157,7 @@ impl KeystoreInput { let mut retval = Keystore { identities: HashMap::new(), contacts: HashMap::new(), - groups: HashMap::new() + groups: HashMap::new(), }; // Iterate through the input keystore file @@ -152,15 +173,18 @@ impl KeystoreInput { for name in contact_names { if let Some(key_string) = self.contacts.get(name) { match PublicKey::from_str(&key_string) { - Ok(pub_key) => { - contacts.push(Contact { - name: Rc::new(name.to_string()), - public_key: pub_key - }); - }, - Err(e) => { - warn!("Unable to add contact named \"{}\" because there was a problem with the public key: {}", name, e); - } + Ok(pub_key) => { + contacts.push(Contact { + name: Rc::new(name.to_string()), + public_key: pub_key, + }); + } + Err(e) => { + warn!( + "Unable to add contact named \"{}\" because there was a problem with the public key: {}", + name, e + ); + } } } } @@ -177,18 +201,21 @@ impl KeystoreInput { name: Rc::new(name.to_string()), private_key, public_key, - secrets: HashSet::new() + secrets: HashSet::new(), }; - i.secrets = contacts.iter().map(|c| { - (c.name.clone(), i.private_key.create_secret(&c.public_key)) - }).collect(); - + i.secrets = contacts + .iter() + .map(|c| (c.name.clone(), i.private_key.create_secret(&c.public_key))) + .collect(); identities.push((i.name.clone(), i)); - }, + } Err(e) => { - warn!("Unable to add identity named \"{}\" because there was a problem with the private key: {}", name, e); + warn!( + "Unable to add identity named \"{}\" because there was a problem with the private key: {}", + name, e + ); } } } @@ -197,9 +224,7 @@ impl KeystoreInput { retval.identities = HashMap::from_iter(identities); // Create a hash of contacts - let contacts = contacts.into_iter().map(|c| { - (c.name.clone(), c) - }); + let contacts = contacts.into_iter().map(|c| (c.name.clone(), c)); retval.contacts = HashMap::from_iter(contacts); @@ -218,136 +243,179 @@ impl KeystoreInput { }); retval.groups = HashMap::from_iter(groups); - + retval } } fn deserialize_private_key<'de, D>(deserializer: D) -> Result<PrivateKey, D::Error> - where D: de::Deserializer<'de> { - +where + D: de::Deserializer<'de>, +{ + use crate::crypto::PrivateKey; use std::str::FromStr; - use crate::crypto::{PrivateKey}; let s: String = de::Deserialize::deserialize(deserializer)?; match PrivateKey::from_str(&s) { Ok(key) => Ok(key), - Err(e) => { - Err(de::Error::custom(format!("{}", e))) - } + Err(e) => Err(de::Error::custom(format!("{}", e))), } } fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error> - where D: de::Deserializer<'de> { - - use hex::decode; +where + D: de::Deserializer<'de>, +{ use crate::crypto::{MeshcoreCryptoError, PublicKey}; + use hex::decode; let s: String = de::Deserialize::deserialize(deserializer)?; match decode(s) { Err(hex::FromHexError::InvalidHexCharacter { c, index }) => { - Err(de::Error::custom(format!("Invalid hex character {} at {} when decoding Public Key.", c, index))) - }, - Err(hex::FromHexError::OddLength) => { - Err(de::Error::custom(format!("Odd number of characters when decoding Public Key."))) - }, - Err(hex::FromHexError::InvalidStringLength) => { - Err(de::Error::custom(format!("Invalid hex string length when decoding Public Key."))) + Err(de::Error::custom(format!( + "Invalid hex character {} at {} when decoding Public Key.", + c, index + ))) } + Err(hex::FromHexError::OddLength) => Err(de::Error::custom(format!( + "Odd number of characters when decoding Public Key." + ))), + Err(hex::FromHexError::InvalidStringLength) => Err(de::Error::custom(format!( + "Invalid hex string length when decoding Public Key." + ))), Ok(hex) => { let key = PublicKey::try_from(hex.as_slice()); match key { Ok(key) => Ok(key), - Err(MeshcoreCryptoError::KeyCreationError) => { - Err(de::Error::custom(format!("Unable to decompress public key points"))) - } - Err(_) => { - Err(de::Error::custom(format!("Invalid hex string length when decoding Public Key."))) - } + Err(MeshcoreCryptoError::KeyCreationError) => Err(de::Error::custom(format!( + "Unable to decompress public key points" + ))), + Err(_) => Err(de::Error::custom(format!( + "Invalid hex string length when decoding Public Key." + ))), } } } } fn deserialize_secret<'de, D>(deserializer: D) -> Result<SharedSecret, D::Error> - where D: de::Deserializer<'de> { - - use std::str::FromStr; +where + D: de::Deserializer<'de>, +{ use crate::crypto::SharedSecret; + use std::str::FromStr; let s: String = de::Deserialize::deserialize(deserializer)?; match SharedSecret::from_str(&s) { Ok(secret) => Ok(secret), - Err(e) => { - Err(de::Error::custom(format!("{}", e))) - } + Err(e) => Err(de::Error::custom(format!("{}", e))), } } // Tests for std operations #[cfg(test)] mod tests { + use hex::decode; + use log::LevelFilter; + use serde::de::IntoDeserializer; + use serde::de::value::{Error as ValueError, StrDeserializer}; use std::io; use std::str::FromStr; use std::sync::mpsc::{Sender, channel}; use std::vec; - use hex::decode; - use log::{LevelFilter}; - use serde::de::IntoDeserializer; - use serde::de::value::{StrDeserializer, Error as ValueError}; - use crate::std_identity::KeystoreInput; - use crate::crypto::*; use super::*; + use crate::crypto::*; + use crate::std_identity::KeystoreInput; #[test] fn secret_deserializer() { - let deserializer: StrDeserializer<ValueError> = "eb50a1bcb3e4e5d7bf69a57c9dada211".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "eb50a1bcb3e4e5d7bf69a57c9dada211".into_deserializer(); let result = deserialize_secret(deserializer); match result { - Ok(result) => assert_eq!(result, SharedSecret::from_str("eb50a1bcb3e4e5d7bf69a57c9dada211").unwrap()), + Ok(result) => assert_eq!( + result, + SharedSecret::from_str("eb50a1bcb3e4e5d7bf69a57c9dada211").unwrap() + ), Err(err) => assert!(false, "Shouldn't have had an error, but got one: {}", err), } // Test failure conditions - let deserializer: StrDeserializer<ValueError> = "eb50a1bcb3e4e5d7bf69a57c9dada21".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "eb50a1bcb3e4e5d7bf69a57c9dada21".into_deserializer(); let result = deserialize_secret(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Try From Slice Error", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Try From Slice Error", + "Unexpected error response: {}", + error.to_string() + ); } #[test] fn public_key_deserializer() { - let deserializer: StrDeserializer<ValueError> = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); match result { - Ok(result) => assert_eq!(result, PublicKey::from_str("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap()), + Ok(result) => assert_eq!( + result, + PublicKey::from_str( + "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f" + ) + .unwrap() + ), Err(err) => assert!(false, "Shouldn't have had an error, but got one: {}", err), } // Test failure conditions - let deserializer: StrDeserializer<ValueError> = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Odd number of characters when decoding Public Key.", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Odd number of characters when decoding Public Key.", + "Unexpected error response: {}", + error.to_string() + ); - let deserializer: StrDeserializer<ValueError> = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec21".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec21".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Invalid hex string length when decoding Public Key.", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Invalid hex string length when decoding Public Key.", + "Unexpected error response: {}", + error.to_string() + ); - let deserializer: StrDeserializer<ValueError> = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "34569df1f9661916901669666fb8025eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Unable to decompress public key points", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Unable to decompress public key points", + "Unexpected error response: {}", + error.to_string() + ); - let deserializer: StrDeserializer<ValueError> = "34569df1f9661916901669666fb8z25eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); + let deserializer: StrDeserializer<ValueError> = + "34569df1f9661916901669666fb8z25eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Invalid hex character z at 28 when decoding Public Key.", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Invalid hex character z at 28 when decoding Public Key.", + "Unexpected error response: {}", + error.to_string() + ); } #[test] @@ -363,12 +431,22 @@ mod tests { let deserializer: StrDeserializer<ValueError> = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E0".into_deserializer(); let result = deserialize_private_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Hex Decode Error", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Hex Decode Error", + "Unexpected error response: {}", + error.to_string() + ); let deserializer: StrDeserializer<ValueError> = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E".into_deserializer(); let result = deserialize_private_key(deserializer); let error = result.unwrap_err(); - assert_eq!(error.to_string(), "Try From Slice Error", "Unexpected error response: {}", error.to_string()); + assert_eq!( + error.to_string(), + "Try From Slice Error", + "Unexpected error response: {}", + error.to_string() + ); } #[test] @@ -394,7 +472,6 @@ mod tests { ]) }; assert_eq!(lhs, rhs); - } // This struct is used as an adaptor, it implements io::Write and forwards the buffer to a mpsc::Sender @@ -432,7 +509,9 @@ mod tests { let _ = pretty_env_logger::env_logger::builder() .is_test(true) .filter_level(LevelFilter::max()) - .target(pretty_env_logger::env_logger::Target::Pipe(Box::new(WriteAdapter { sender: rx }))) + .target(pretty_env_logger::env_logger::Target::Pipe(Box::new( + WriteAdapter { sender: rx }, + ))) .try_init(); // Test failure conditions @@ -459,11 +538,12 @@ mod tests { .zip(expect.into_iter()) .for_each(|(got, expect)| { println!("{}", &got); - assert!(got.contains(expect), "Got log line: \"{}\" and didn't find \"{}\" within it as expected.", got, expect); - } - ); - + assert!( + got.contains(expect), + "Got log line: \"{}\" and didn't find \"{}\" within it as expected.", + got, + expect + ); + }); } - - -}
\ No newline at end of file +} diff --git a/src/string_helper.rs b/src/string_helper.rs index e5af42b..a18bfcd 100644 --- a/src/string_helper.rs +++ b/src/string_helper.rs @@ -1,5 +1,8 @@ #[cfg(not(feature = "std"))] -use arraystring::{ArrayString, typenum::{U32, U160}}; +use arraystring::{ + ArrayString, + typenum::{U32, U160}, +}; #[cfg(feature = "std")] pub(crate) type NameString = String; @@ -8,7 +11,7 @@ pub(crate) type NameString = String; pub(crate) fn name_string_from_slice(s: &[u8]) -> NameString { match String::from_utf8(s.to_vec()) { Ok(s) => s, - Err(_) => "Unable to create string".to_string() + Err(_) => "Unable to create string".to_string(), } } @@ -32,10 +35,8 @@ pub(crate) fn message_string_from_slice(s: &[u8]) -> MessageString { if let Some(s) = split.next() { match String::from_utf8(s.to_vec()) { - Ok(s) => { - s.trim().to_string() - } - Err(_) => "Unable to create string".to_string() + Ok(s) => s.trim().to_string(), + Err(_) => "Unable to create string".to_string(), } } else { "".to_string() @@ -60,7 +61,7 @@ pub(crate) type PasswordString = ArrayString<U160>; pub(crate) fn password_string_from_slice(s: &[u8]) -> PasswordString { match String::from_utf8(s.to_vec()) { Ok(s) => s, - Err(_) => "".to_string() + Err(_) => "".to_string(), } } 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 +} diff --git a/src/trace.rs b/src/trace.rs index 3770702..b25aad7 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -9,7 +9,7 @@ pub struct Trace { pub(crate) path_snr: ArrayVec<[f32; 64]>, invalid: bool, - pub(crate) temp_path: ArrayVec<[u16; 64]> + pub(crate) temp_path: ArrayVec<[u16; 64]>, } impl From<Bytes> for Trace { @@ -25,17 +25,19 @@ impl From<Bytes> for Trace { temp_path: ArrayVec::new(), }; - if bytes.len() < 10 { return trace; } + if bytes.len() < 10 { + return trace; + } - trace.tag = bytes.get_u32(); - trace.auth = bytes.get_u32(); - trace.flags = bytes.get_u8(); + trace.tag = bytes.get_u32(); + trace.auth = bytes.get_u32(); + trace.flags = bytes.get_u8(); for byte in bytes.iter() { trace.temp_path.push(*byte as u16); } - trace.invalid = false; + trace.invalid = false; trace } } @@ -53,13 +55,12 @@ impl core::fmt::Display for Trace { // Tests for std operations #[cfg(test)] mod tests { + use crate::{packet::*, packet_content::PacketContent}; use core::str::FromStr; use hex::decode; use tinyvec::array_vec; - use crate::{packet::*, packet_content::PacketContent}; - - use super::*; + use super::*; #[test] fn trace() { @@ -78,9 +79,8 @@ mod tests { path_snr: array_vec!([f32; 64] => 11.0, -8.0, -3.75), invalid: false, temp_path: array_vec!([u16; 64] => 0xAC, 0xA0, 0x79), - } - ), - incomplete: false + }), + incomplete: false, }; let rhs_packet = Packet::from_str(sample).unwrap(); @@ -88,9 +88,9 @@ mod tests { // We can only check the description in std mode #[cfg(feature = "std")] - assert_eq!(format!("{}", rhs_packet), " Direct | v1 | | [ac, a0, 79] | | TRACE | (ac): 11dB (a0): -8dB (79): -3.75dB ") - + assert_eq!( + format!("{}", rhs_packet), + " Direct | v1 | | [ac, a0, 79] | | TRACE | (ac): 11dB (a0): -8dB (79): -3.75dB " + ) } - - -}
\ No newline at end of file +} |
