diff --git a/Cargo.lock b/Cargo.lock index 59d67e5..9cfdad9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,6 +437,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "ff" version = "0.13.0" @@ -579,6 +589,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "inout" version = "0.1.3" @@ -593,6 +609,7 @@ name = "irc-e2e" version = "0.1.0" dependencies = [ "base64", + "eyre", "ircparser", "openssl", "pgp", diff --git a/Cargo.toml b/Cargo.toml index 2dbac81..9ae21de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] base64 = "0.21.4" +eyre = "0.6.8" ircparser = "0.2.1" openssl = "0.10" pgp = "0.10.2" diff --git a/src/client_handler.rs b/src/client_handler.rs new file mode 100644 index 0000000..04f6479 --- /dev/null +++ b/src/client_handler.rs @@ -0,0 +1,49 @@ +use crate::helpers::bytes_to_privmsg_base64; +use crate::{encryption, helpers}; +use pgp::{Deserializable, SignedPublicKey}; +use std::collections::HashMap; +use std::sync::mpsc::{Receiver, Sender}; +use eyre::Result; + +pub fn handle_message_from_client( + recieved: &str, + public_key: &Vec, + server: &str, + keys: &mut HashMap, + writer_channel_tx: &Sender, + writer_channel_rx: &Receiver, + listener_channel_tx: &Sender, + _listener_channel_rx: &Receiver, +) -> Result<()> { + let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0]; + + if command.command == "PRIVMSG" && !command.params[0].starts_with("#") { + let other = &command.params[0]; + + if !keys.contains_key(other) { + helpers::send_key(writer_channel_tx, other, public_key)?; + let key = helpers::recieve_message_base64( + writer_channel_rx, + listener_channel_tx, + "127.0.0.1", + server, + &other, + "END_KEY", + )?; + let key = SignedPublicKey::from_bytes(key.as_slice())?; + keys.insert(other.to_string(), key); + } + + let foreign_key = keys.get(other).unwrap(); + + writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?; + writer_channel_tx.send(bytes_to_privmsg_base64( + &encryption::encrypt(&foreign_key, &command.params[1])?, + other, + ))?; + writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?; + } else { + writer_channel_tx.send(recieved.replace("127.0.0.1", &server))?; + } + Ok(()) +} diff --git a/src/encryption.rs b/src/encryption.rs new file mode 100644 index 0000000..03b5882 --- /dev/null +++ b/src/encryption.rs @@ -0,0 +1,29 @@ +use pgp::crypto::sym::SymmetricKeyAlgorithm; +use pgp::ser::Serialize; +use pgp::{Deserializable, Message, SignedPublicKey, SignedSecretKey}; +use rand::prelude::*; +use eyre::Result; + +pub fn encrypt(key: &SignedPublicKey, message: &str) -> Result, pgp::errors::Error> { + let message = Message::new_literal("none", message); + let mut rng = StdRng::from_entropy(); + + let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?; + + Ok(message.to_bytes()?) +} + +pub fn decrypt<'a>( + key: &'a SignedSecretKey, + message: Vec, + password: &'a str, +) -> Result { + let message = Message::from_bytes(message.as_slice())?; // Convert message bytes to message object + let message = message.decrypt(|| password.to_string(), &[key])?.0; // Decrypt + let message = message.map(|x| x.unwrap()).collect::>(); // Get all messages + let message = &message[0]; // Get first message + let message = message.get_content()?.unwrap_or(Vec::new()); // Get message content as Vec + let message = String::from_utf8(message).unwrap(); // Convert to String + + Ok(message) +} diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..59efd00 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,92 @@ +use base64::{engine::general_purpose, Engine as _}; +use std::sync::mpsc::{self, Receiver, Sender}; +use eyre::Result; +use ircparser; + +static MAX_LENGTH: usize = 300; + +fn forward( + message: &str, + stream: &Sender, + server_local: &str, + server_forward: &str, +) -> Result<(), mpsc::SendError> { + if ircparser::parse(message).unwrap()[0].command == "PRIVMSG" { + return Ok(()) + } + stream.send(message.replace(&server_local, server_forward)) +} + +pub fn bytes_to_privmsg_base64(message: &Vec, reciever: &str) -> String { + let message_length = MAX_LENGTH - format!("PRIVMSG {reciever} :\r\n").len(); + let encoded = general_purpose::STANDARD.encode(message); + + let mut command = String::new(); + for line in encoded + .chars() + .collect::>() + .chunks(message_length) + .map(|c| c.iter().collect::()) + { + dbg!(&line); + command.push_str(&format!("PRIVMSG {reciever} :{line}\r\n")); + } + + println!("{}", encoded); + println!("{}", command); + command +} + +pub fn send_key( + sender: &Sender, + reciever: &str, + key: &Vec, +) -> Result<()> { + sender.send(format!("PRIVMSG {reciever} :START_KEY\r\n"))?; + sender.send(bytes_to_privmsg_base64(key, reciever))?; + sender.send(format!("PRIVMSG {reciever} :END_KEY\r\n"))?; + + Ok(()) +} + +pub fn get_nick(userstring: &str) -> String { + let userstring = userstring.chars().collect::>(); + let start_pos = userstring.iter().position(|&x| x == ':').unwrap() + 1; + let end_pos = userstring.iter().position(|&x| x == '!').unwrap(); + userstring[start_pos..end_pos].iter().collect::() +} + +pub fn recieve_message_base64( + writer_channel_rx: &Receiver, + forward_stream: &Sender, + server_local: &str, + server_forward: &str, + sender: &str, + end: &str, +) -> Result> { + let mut message: Vec = Vec::new(); + + while !message.contains(&end.to_string()) { + let recieved_raw = writer_channel_rx.recv()?; + + let recieved = &ircparser::parse(&recieved_raw).unwrap()[0]; + let begin_source_reciever = format!(":{sender}!"); + if recieved.command != "PRIVMSG" + || !recieved + .source + .clone() + .unwrap_or("".to_string()) + .starts_with(&begin_source_reciever) || recieved.params[0].starts_with("#") + { + forward(&recieved_raw, forward_stream, server_local, server_forward)?; + continue; + } + + message.push(recieved.params[1].clone()); + } + message.pop(); + + let foreign_key = dbg!(message.concat()); + let foreign_key = general_purpose::STANDARD.decode(foreign_key)?; + Ok(foreign_key) +} diff --git a/src/listener_server.rs b/src/listener_server.rs index 56e449b..65319ff 100644 --- a/src/listener_server.rs +++ b/src/listener_server.rs @@ -1,6 +1,8 @@ use std::io::{ErrorKind, Read, Write}; use std::net::TcpListener; use std::sync::mpsc; +use std::thread; +use std::time::Duration; pub fn listen_to_client(tx: mpsc::Sender, rx: mpsc::Receiver, port: &str) { let listener = TcpListener::bind("127.0.0.1:".to_string() + port).unwrap(); @@ -19,7 +21,7 @@ pub fn listen_to_client(tx: mpsc::Sender, rx: mpsc::Receiver, po match stream.read(&mut buf) { Ok(_length) => buffer.push(buf[0]), Err(_error) => match _error.kind() { - ErrorKind::WouldBlock => {}, + ErrorKind::WouldBlock => {} _ => { dbg!(_error); } @@ -37,5 +39,6 @@ pub fn listen_to_client(tx: mpsc::Sender, rx: mpsc::Receiver, po } let _ = tx.send(String::from_utf8_lossy(&buffer).to_string()); + thread::sleep(Duration::from_millis(100)); } } diff --git a/src/main.rs b/src/main.rs index a3eb8f5..2e45a1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,81 +1,20 @@ -use base64::{engine::general_purpose, Engine as _}; -use ircparser; -use pgp::crypto::sym::SymmetricKeyAlgorithm; -use pgp::ser::Serialize; -use pgp::{Deserializable, Message, SignedPublicKey, SignedSecretKey}; -use rand::prelude::*; +use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; use std::collections::HashMap; use std::net::{Shutdown, TcpStream}; use std::sync::mpsc; use std::thread; use std::time::Duration; use std::{env, fs}; +use eyre::Result; +mod client_handler; +mod encryption; +mod helpers; mod listener_server; +mod server_handler; mod writer_client; -macro_rules! try_recv { - ($rx:ident) => { - match $rx.try_recv() { - Ok(recieved) => recieved, - Err(_e) => match _e { - mpsc::TryRecvError::Empty => String::new(), - mpsc::TryRecvError::Disconnected => { - panic!("Error!") - } - }, - } - }; -} - -fn bytes_to_privmsg_base64(message: Vec, reciever: &str) -> String { - let mut command = String::new(); - for line in general_purpose::STANDARD - .encode(message) - .chars() - .collect::>() - .chunks(500) - .map(|c| c.iter().collect::()) - { - command.push_str(&format!("PRIVMSG {reciever} :{line}\r\n")); - } - - println!("{}", command); - command -} - -fn encrypt(key: &SignedPublicKey, message: &str) -> Result, pgp::errors::Error> { - let message = Message::new_literal("none", message); - let mut rng = StdRng::from_entropy(); - - let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?; - - Ok(message.to_bytes()?) -} - -fn decrypt<'a>( - key: &'a SignedSecretKey, - message: Vec, - password: &'a str, -) -> Result { - let message = Message::from_bytes(message.as_slice())?; // Convert message bytes to message object - let message = message.decrypt(|| password.to_string(), &[key])?.0; // Decrypt - let message = message.map(|x| x.unwrap()).collect::>(); // Get all messages - let message = &message[0]; // Get first message - let message = message.get_content()?.unwrap_or(Vec::new()); // Get message content as Vec - let message = String::from_utf8(message).unwrap(); // Convert to String - - Ok(message) -} - -fn get_nick(userstring: &str) -> String { - let userstring = userstring.chars().collect::>(); - let start_pos = userstring.iter().position(|&x| x == ':').unwrap() + 1; - let end_pos = userstring.iter().position(|&x| x == '!').unwrap(); - userstring[start_pos..end_pos].iter().collect::() -} - -fn main() -> Result<(), Box> { +fn main() -> Result<()> { let mut args = env::args().collect::>(); let server = args[1].clone(); @@ -126,161 +65,44 @@ fn main() -> Result<(), Box> { }); let mut keys: HashMap = HashMap::new(); - let mut userstring = String::new(); loop { - let recieved = try_recv!(listener_channel_rx); + match listener_channel_rx.try_recv() { + Ok(message) => client_handler::handle_message_from_client( + &message, + &public_key, + &server, + &mut keys, + &writer_channel_tx, + &writer_channel_rx, + &listener_channel_tx, + &listener_channel_rx, + )?, + Err(error) => match error { + mpsc::TryRecvError::Empty => {} + mpsc::TryRecvError::Disconnected => panic!("listener_channel_rx disconnected"), + }, + }; - if !recieved.is_empty() { - let command = &ircparser::parse(&recieved).expect("Got an invalid IRC instruction")[0]; + match writer_channel_rx.try_recv() { + Ok(message) => server_handler::handle_message_from_server( + &message, + &public_key, + &secret_key, + &server, + &passwd, + &mut keys, + &writer_channel_tx, + &writer_channel_rx, + &listener_channel_tx, + &listener_channel_rx, + )?, + Err(error) => match error { + mpsc::TryRecvError::Empty => {} + mpsc::TryRecvError::Disconnected => panic!("writer_channel_rx disconnected"), + }, + }; - if command.command == "PRIVMSG" && !command.params[0].starts_with("#") { - let reciever = &command.params[0]; - if !keys.contains_key(reciever) { - writer_channel_tx.send(format!("PRIVMSG {reciever} :START_KEY\r\n"))?; - - writer_channel_tx - .send(bytes_to_privmsg_base64(public_key.clone(), reciever))?; - writer_channel_tx.send(format!("PRIVMSG {reciever} :END_KEY\r\n"))?; - - let mut foreign_key: Vec = Vec::new(); - while !foreign_key.contains(&"END_KEY".to_string()) { - let recieved_raw = writer_channel_rx.recv()?; - - let recieved = &ircparser::parse(&recieved_raw).unwrap()[0]; - let begin_source_reciever = format!(":{reciever}!"); - if recieved.command != "PRIVMSG" - || !recieved - .source - .clone() - .unwrap_or("".to_string()) - .starts_with(&begin_source_reciever) - { - listener_channel_tx.send(recieved_raw)?; - continue; - } - - foreign_key.push(recieved.params[1].clone()); - } - foreign_key.pop(); - - println!("Got a foreign key from {reciever}"); - let foreign_key = foreign_key.concat(); - dbg!(&foreign_key); - - let foreign_key = general_purpose::STANDARD.decode(foreign_key)?; - - dbg!("It's decoded"); - - let key = SignedPublicKey::from_bytes(foreign_key.as_slice())?; - - dbg!("And I now got it's SignedPublicKey format"); - - keys.insert(reciever.to_string(), key); - } - - let foreign_key = keys.get(reciever).unwrap(); - - writer_channel_tx.send(format!("PRIVMSG {reciever} START_MESSAGE\r\n"))?; - writer_channel_tx.send(bytes_to_privmsg_base64( - encrypt(&foreign_key, &command.params[1])?, - reciever, - ))?; - writer_channel_tx.send(format!("PRIVMSG {reciever} END_MESSAGE\r\n"))?; - } else { - writer_channel_tx.send(recieved.replace("127.0.0.1", &server))?; - } - } - - let recieved = try_recv!(writer_channel_rx); - if !recieved.is_empty() { - let recieved_parsed = &ircparser::parse(&recieved).unwrap()[0]; - - if recieved_parsed.command != "PRIVMSG" - || recieved_parsed - .params - .get(0) - .unwrap_or(&String::new()) - .starts_with("#") - { - listener_channel_tx.send(recieved.replace(&server, "127.0.0.1"))?; - continue; - } - - dbg!(&recieved_parsed); - - if recieved_parsed.params[1] == "START_MESSAGE" { - let reciever = get_nick(&recieved_parsed.source.clone().unwrap()); - let sample_privmsg = recieved.clone(); - - let mut encrypted_message: Vec = Vec::new(); - while !encrypted_message.contains(&"END_MESSAGE".to_string()) { - let recieved_raw = writer_channel_rx.recv()?; - let recieved = &ircparser::parse(&recieved_raw).unwrap()[0]; - - let begin_source_reciever = format!(":{reciever}!"); - if recieved.command != "PRIVMSG" - || !recieved - .source - .clone() - .unwrap_or("".to_string()) - .starts_with(&begin_source_reciever) - { - listener_channel_tx.send(recieved_raw.replace(&server, "127.0.0.1"))?; - continue; - } - - encrypted_message.push(recieved.params[1].clone()); - } - encrypted_message.pop(); - - let encrypted_message = encrypted_message.join(""); - let encrypted_message = general_purpose::STANDARD.decode(encrypted_message)?; - - let decrypted_message = decrypt(&secret_key, encrypted_message, &passwd)?; - listener_channel_tx - .send(sample_privmsg.replace("START_MESSAGE", &decrypted_message))?; - } else if recieved_parsed.params[1] == "START_KEY" { - let reciever = get_nick(&recieved_parsed.source.clone().unwrap()); - let to_send = bytes_to_privmsg_base64(public_key.clone(), &reciever); - writer_channel_tx.send(to_send)?; - writer_channel_tx.send(format!("PRIVMSG {reciever} END_KEY\r\n"))?; - - dbg!("Started Getting a key"); - let reciever = get_nick(&recieved_parsed.source.clone().unwrap()); - let mut foreign_key: Vec = Vec::new(); - while !foreign_key.contains(&"END_KEY".to_string()) { - let recieved_raw = writer_channel_rx.recv()?; - - let recieved = &ircparser::parse(&recieved_raw).unwrap()[0]; - let begin_source_reciever = format!(":{reciever}!"); - if recieved.command != "PRIVMSG" - || !recieved - .source - .clone() - .unwrap_or("".to_string()) - .starts_with(&begin_source_reciever) - { - listener_channel_tx.send(recieved_raw.replace(&server, "127.0.0.1"))?; - continue; - } - - foreign_key.push(recieved.params[1].clone()); - } - foreign_key.pop(); - dbg!("Got a foreign key from"); - dbg!(&reciever); - - let foreign_key = foreign_key.concat(); - - let foreign_key = general_purpose::STANDARD.decode(foreign_key)?; - dbg!("Decoded the key"); - - let key = SignedPublicKey::from_bytes(foreign_key.as_slice())?; - dbg!("Deserialized the key"); - - keys.insert(reciever.to_string(), key); - } - } + thread::sleep(Duration::from_millis(100)); } } diff --git a/src/server_handler.rs b/src/server_handler.rs new file mode 100644 index 0000000..bc3138f --- /dev/null +++ b/src/server_handler.rs @@ -0,0 +1,77 @@ +use crate::{encryption, helpers}; +use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; +use std::collections::HashMap; +use std::sync::mpsc::{self, Receiver, Sender}; +use eyre::Result as Result; + +fn forward( + message: &str, + listener_channel_tx: &Sender, + server: &str, +) -> Result<(), mpsc::SendError> { + listener_channel_tx.send(message.replace(&server, "127.0.0.1")) +} + +pub fn handle_message_from_server( + recieved: &str, + public_key: &Vec, + secret_key: &SignedSecretKey, + server: &str, + passwd: &str, + keys: &mut HashMap, + writer_channel_tx: &Sender, + writer_channel_rx: &Receiver, + listener_channel_tx: &Sender, + _listener_channel_rx: &Receiver, +) -> Result<()> { + let recieved_parsed = &ircparser::parse(&recieved).unwrap()[0]; + + if recieved_parsed.command != "PRIVMSG" + || recieved_parsed + .params + .get(0) + .unwrap_or(&String::new()) + .starts_with("#") + { + forward(recieved, listener_channel_tx, server)?; + return Ok(()); + } + + dbg!(&recieved_parsed); + + if recieved_parsed.params[1] == "START_MESSAGE" { + let sender = helpers::get_nick(&recieved_parsed.source.clone().unwrap()); + + let message = helpers::recieve_message_base64( + writer_channel_rx, + listener_channel_tx, + &server, + "127.0.0.1", + &sender, + "END_MESSAGE", + )?; // Get + let message = encryption::decrypt(&secret_key, message, &passwd)?; // Decrypt + + listener_channel_tx.send(recieved.replace("START_MESSAGE", &message))?; // Send + } else if recieved_parsed.params[1] == "START_KEY" { + let sender = helpers::get_nick(&recieved_parsed.source.clone().unwrap()); + let to_send = helpers::bytes_to_privmsg_base64(&public_key, &sender); + writer_channel_tx.send(to_send)?; + writer_channel_tx.send(format!("PRIVMSG {sender} END_KEY\r\n"))?; + + let foreign_key = helpers::recieve_message_base64( + writer_channel_rx, + listener_channel_tx, + &server, + "127.0.0.1", + &sender, + "END_KEY", + )?; + dbg!(&foreign_key); + let key = SignedPublicKey::from_bytes(foreign_key.as_slice())?; + println!("Got a key from {sender}"); + keys.insert(sender.to_string(), key); + } + + Ok(()) +} diff --git a/src/writer_client.rs b/src/writer_client.rs index aec3453..dfe2bb3 100644 --- a/src/writer_client.rs +++ b/src/writer_client.rs @@ -2,6 +2,8 @@ use openssl::ssl::{SslConnector, SslMethod}; use std::io::{ErrorKind, Write}; use std::net::TcpStream; use std::sync::mpsc; +use std::thread; +use std::time::Duration; pub fn write_to_server( tcp_stream: TcpStream, @@ -52,5 +54,6 @@ pub fn write_to_server( } let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string())); + thread::sleep(Duration::from_millis(100)); } }