diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/crypto.rs | 58 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/packet.rs | 259 |
3 files changed, 320 insertions, 0 deletions
diff --git a/src/crypto.rs b/src/crypto.rs new file mode 100644 index 0000000..adcd4d8 --- /dev/null +++ b/src/crypto.rs @@ -0,0 +1,58 @@ +use std::{collections::HashMap, str::FromStr}; + +use ed25519_dalek::hazmat::ExpandedSecretKey; +use curve25519_dalek::{constants, edwards::CompressedEdwardsY}; +use tokio_util::bytes::Bytes; +use x25519_dalek::StaticSecret; + +use hex::decode; + +pub struct PrivateKey(ExpandedSecretKey); +pub struct PublicKey(CompressedEdwardsY); +pub struct SharedSecret([u8; 32]); + +impl FromStr for PrivateKey { + type Err = hex::FromHexError; + + fn from_str(hex_str: &str) -> Result<Self, Self::Err> { + let hex = decode(hex_str)?; + + if let Ok(bytes) = TryInto::<[u8; 64]>::try_into(hex) { + let exp_sec_key = ExpandedSecretKey::from_bytes(&bytes); + + Ok(PrivateKey(exp_sec_key)) + } else { + Err(hex::FromHexError::InvalidStringLength) + } + } +} + +impl From<PrivateKey> for PublicKey { + fn from(key: PrivateKey) -> Self { + PublicKey((key.0.scalar * constants::ED25519_BASEPOINT_POINT).compress()) + } +} + +impl PrivateKey { + pub fn create_secret(&self, other: &PublicKey) -> SharedSecret { + let private = StaticSecret::from(*self.0.scalar.as_bytes()); + SharedSecret(private.diffie_hellman(&other.0.to_bytes().into()).to_bytes()) + } +} + +impl SharedSecret { + pub fn mac_then_decrypt(&self, mac: [u8; 2], data: Bytes) -> Option<Bytes> { + None + } + + pub fn encrypt_then_mac(&self, data: Bytes) -> Option<([u8; 2], Bytes)> { + None + } +} + +pub struct Keychain { + pub private_keys: Vec<PrivateKey>, + pub public_keys: Vec<PublicKey>, + + // secrets: HashMap<String, Ha> +}
\ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..817b464 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod crypto; +pub mod packet; + diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000..d44aa66 --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,259 @@ +use std::str::FromStr; + +use hex::decode; +use tokio_util::bytes::{Buf, Bytes}; + +use crate::{crypto::SharedSecret, packet}; + +#[derive(PartialEq, Debug)] +struct Packet { + route_type: RouteType, + version: PayloadVersion, + path: Vec<u16>, + transport: [u16; 2], + raw_content: Bytes, + content: PacketContent, + incomplete: bool +} + +#[derive(PartialEq, Debug)] +enum RouteType { + TransportFlood, + Flood, + Direct, + TransportDirect, + Invalid, +} + +impl From<u8> for RouteType { + fn from(value: u8) -> Self { + match value & 0x03 { + 0x00 => RouteType::TransportFlood, + 0x01 => RouteType::Flood, + 0x02 => RouteType::Direct, + 0x03 => RouteType::TransportDirect, + _ => RouteType::Invalid + } + } +} + +#[derive(PartialEq, Debug)] +enum PayloadVersion { + VersionOne, + VersionTwo, + VersionThree, + VersionFour, + Invalid, +} + +impl From<u8> for PayloadVersion { + fn from(value: u8) -> Self { + let value = (value & 0xC0) >> 6; + assert!(value < 4, "Programming error in masking and bitshifting"); + + match value { + 0x00 => PayloadVersion::VersionOne, + 0x01 => PayloadVersion::VersionTwo, + 0x02 => PayloadVersion::VersionThree, + 0x03 => PayloadVersion::VersionFour, + _ => PayloadVersion::Invalid + } + } +} + +#[derive(PartialEq, Debug)] +enum PacketContent { + Request(Request), + Response(Response), + Text(Text), + Ack(Ack), + Advert(Advert), + GroupText(GroupText), + GroupData(GroupData), + AnonReq(AnonReq), + Path(Path), + Trace(Trace), + Multipart(MultiPart), + Raw(Raw), + + Invalid, +} + +#[derive(PartialEq, Debug)] +struct Request {} + +#[derive(PartialEq, Debug)] +struct Response {} + +#[derive(PartialEq, Debug)] +struct Text {} + +#[derive(PartialEq, Debug)] +struct Ack {} + +#[derive(PartialEq, Debug)] +struct Advert {} + +#[derive(PartialEq, Debug)] +struct GroupText {} + +#[derive(PartialEq, Debug)] +struct GroupData {} + +#[derive(PartialEq, Debug)] +struct AnonReq {} + +#[derive(PartialEq, Debug)] +struct Path {} + +#[derive(PartialEq, Debug)] +struct Trace {} + +#[derive(PartialEq, Debug)] +struct MultiPart {} + +#[derive(PartialEq, Debug)] +struct Raw {} + +impl FromStr for Packet { + type Err = hex::FromHexError; + + fn from_str(hex_str: &str) -> Result<Self, Self::Err> { + let hex = decode(hex_str)?; + Ok(Packet::from(Bytes::copy_from_slice(&hex))) + } +} + +impl From<Bytes> for Packet { + fn from(bytes: Bytes) -> Self { + let mut bytes = bytes; + + // This is the packet we'll build as we begin to parse + let mut packet = Packet::default(); + + if bytes.len() == 0 { + return packet + } + + let header = bytes.split_to(0).get_u8(); + + // Parse the header byte + packet.route_type = RouteType::from(header); + packet.version = PayloadVersion::from(header); + + // Get the transport if it's provided + 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; } + + [bytes.split_to(2).get_u16(), bytes.split_to(2).get_u16()] + }, + _ => { + [0, 0] + } + }; + + // Get the route + if bytes.len() == 0 { return packet; } + let path_length = bytes.split_to(1).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; } + + let route: Vec<u16> = bytes + .split_to(path_length) + .into_iter() + .map(|x| x as u16) + .collect(); + route + }, + PayloadVersion::VersionTwo => { + // The packet isn't long enough for the indicated route + if bytes.len() < path_length as usize * 2 { return packet; } + + let route: Vec<u16> = bytes + .split_to(path_length * 2) + .chunks(2) + .map(|c| Bytes::copy_from_slice(c).get_u16()) + .collect(); + + route + }, + _ => { + return packet; + } + }; + + // Get the rest of the payload and subscript the parsing to the other structs + packet.raw_content = bytes; + + // Mark the packet as complete and valid + packet.incomplete = false; + + packet + } +} + +impl Packet { + pub fn attempt_decrypt(&mut self, secret: SharedSecret) -> bool { + false + } + +} + +impl Default for Packet { + fn default() -> Self { + Packet { + route_type: RouteType::Invalid, + version: PayloadVersion::Invalid, + path: vec![], + transport: [0, 0], + raw_content: Bytes::new(), + content: PacketContent::Invalid, + incomplete: true + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn header_route_type() { + // The route type is the lowest-order two bits + assert!(RouteType::TransportFlood == RouteType::from(0x00)); + assert!(RouteType::TransportFlood == RouteType::from(0xFC)); + assert!(RouteType::Flood == RouteType::from(0x01)); + assert!(RouteType::Flood == RouteType::from(0xFD)); + assert!(RouteType::Direct == RouteType::from(0x02)); + assert!(RouteType::Direct == RouteType::from(0xFE)); + assert!(RouteType::TransportDirect == RouteType::from(0x03)); + assert!(RouteType::TransportDirect == RouteType::from(0xFF)); + } + + #[test] + fn header_version() { + assert!(PayloadVersion::VersionOne == PayloadVersion::from(0x00)); + assert!(PayloadVersion::VersionOne == PayloadVersion::from(0x3F)); + assert!(PayloadVersion::VersionTwo == PayloadVersion::from(0x40)); + assert!(PayloadVersion::VersionTwo == PayloadVersion::from(0x7F)); + assert!(PayloadVersion::VersionThree == PayloadVersion::from(0x80)); + assert!(PayloadVersion::VersionThree == PayloadVersion::from(0xBF)); + assert!(PayloadVersion::VersionFour == PayloadVersion::from(0xC0)); + assert!(PayloadVersion::VersionFour == PayloadVersion::from(0xFF)); + } + + #[test] + fn packet() { + // Check the hex decode errors + assert!(Err(hex::FromHexError::InvalidHexCharacter { c: 's', index: 0 }) == Packet::from_str("s0")); + assert!(Err(hex::FromHexError::OddLength) == Packet::from_str("0")); + + // Check errors related to packet length issues + assert!(Packet::default() == Packet::from_str("").unwrap()) + } +}
\ No newline at end of file |
