Compare commits

..

No commits in common. "d38da05d0539259e9bc64680ecc2276c90f99c93" and "e19e3b0d253f3a62faa918d571228c5a98ee5372" have entirely different histories.

9 changed files with 1452 additions and 89 deletions

4
.gitignore vendored
View file

@ -1,2 +1,4 @@
/target /target
Cargo.lock secret.gpg
public.gpg
.env

1406
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,15 @@
[package] [package]
name = "e2e-irc" name = "e2e-irc"
version = "1.0.2" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
argparse = "0.2.2"
base64 = "0.21.4" base64 = "0.21.4"
dirs = "5.0.1" dotenv = "0.15.0"
eyre = "0.6.8" eyre = "0.6.8"
ircparser = "0.2.1" ircparser = "0.2.1"
openssl = "0.10" openssl = "0.10"
pgp = "0.10.2" pgp = "0.10.2"
rand = "0.8.5" rand = "0.8.5"
toml = "0.8.2"

View file

@ -1,35 +1,25 @@
# e2e-irc # e2e-irc
This is an IRC bouncer that supports end-to-end encryption and is horribly written. This is an IRC bouncer that supports end-to-end encryption and is horribly written.
# Configuration # Setup CWD
e2e-irc uses a config file in `~/.config/e2e-irc/config.toml` on linux. To use this you need to build it and in your CWD you need to add two files. One called `secret.gpg` and one called `public.gpg` that are just pgp keys.
The configuration file is a toml file with two or three fields: You also need a .env file in your CWD like this:
- public_key the location of the public pgp key
- secret_key the location of the secret pgp key
- passwd the password of the pgp key
# Usage
```
e2e-irc [OPTIONS] SERVER
Encrypted IRC Bouncer
Positional arguments:
server The Address Of The Server The Bouncer Connects To
Optional arguments:
-h,--help Show this help message and exit
-p,--port PORT The Port The Bouncer Binds To
--sp,--server-port SERVER_PORT
The TLS Enabled Port Of The Server
```
# Install
```bash ```bash
cargo install --git https://forgejo.vanten-s.com/vanten-s/e2e-irc.git SERVER={address of the server you want to connect to (has to support tls)}
PORT={the port you want the program to bind to and the port that the IRC client should connect to}
PASSWD={password of your pgp key} # Optional
``` ```
# Run # Run
Then you just run the binary with:
`cargo run --release`
or build the project to get a binary
```bash ```bash
e2e-irc [OPTIONS] SERVER cargo build --release
cp target/release/e2e-irc ~/.local/bin/
``` ```
and after running that once you can run the program with
```bash
e2e-irc
```
but you have to have the .env in the CWD

View file

@ -17,7 +17,7 @@ pub fn handle_message_from_client(
) -> Result<()> { ) -> Result<()> {
let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0]; let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0];
if command.command == "PRIVMSG" && !command.params[0].starts_with('#') { if command.command == "PRIVMSG" && !command.params[0].starts_with("#") {
let other = &command.params[0]; let other = &command.params[0];
if !keys.contains_key(other) { if !keys.contains_key(other) {
@ -27,7 +27,7 @@ pub fn handle_message_from_client(
listener_channel_tx, listener_channel_tx,
"127.0.0.1", "127.0.0.1",
server, server,
other, &other,
"END_KEY", "END_KEY",
)?; )?;
let key = SignedPublicKey::from_bytes(key.as_slice())?; let key = SignedPublicKey::from_bytes(key.as_slice())?;
@ -38,12 +38,12 @@ pub fn handle_message_from_client(
writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?; writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?;
writer_channel_tx.send(bytes_to_privmsg_base64( writer_channel_tx.send(bytes_to_privmsg_base64(
&encryption::encrypt(foreign_key, &command.params[1])?, &encryption::encrypt(&foreign_key, &command.params[1])?,
other, other,
))?; ))?;
writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?; writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?;
} else { } else {
writer_channel_tx.send(recieved.replace("127.0.0.1", server))?; writer_channel_tx.send(recieved.replace("127.0.0.1", &server))?;
} }
Ok(()) Ok(())
} }

View file

@ -10,7 +10,7 @@ pub fn encrypt(key: &SignedPublicKey, message: &str) -> Result<Vec<u8>, pgp::err
let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?; let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?;
message.to_bytes() Ok(message.to_bytes()?)
} }
pub fn decrypt<'a>( pub fn decrypt<'a>(

View file

@ -1,5 +1,6 @@
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
use eyre::Result; use eyre::Result;
use ircparser;
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
#[derive(Debug)] #[derive(Debug)]
@ -44,7 +45,7 @@ fn forward(
match ircparser::parse(&message) { match ircparser::parse(&message) {
Ok(val) => match val[0].command.as_str() { Ok(val) => match val[0].command.as_str() {
"PRIVMSG" => stream.send(message), "PRIVMSG" => stream.send(message),
_ => stream.send(message.replace(server_local, server_forward)), _ => stream.send(message.replace(&server_local, server_forward)),
}, },
Err(_) => stream.send(message.replace(server_local, server_forward)), Err(_) => stream.send(message.replace(server_local, server_forward)),
} }
@ -112,7 +113,7 @@ pub fn recieve_message_base64(
.clone() .clone()
.unwrap_or("".to_string()) .unwrap_or("".to_string())
.starts_with(&begin_source_reciever) .starts_with(&begin_source_reciever)
|| recieved.params[0].starts_with('#') || recieved.params[0].starts_with("#")
{ {
forward(recieved_raw, forward_stream, server_local, server_forward)?; forward(recieved_raw, forward_stream, server_local, server_forward)?;
continue; continue;

View file

@ -31,13 +31,13 @@ fn stream_handler(tx: &mpsc::Sender<String>, rx: &mpsc::Receiver<String>, mut st
} }
}; };
} }
Err(TryRecvError::Empty) => {} Err(TryRecvError::Empty) => {},
Err(TryRecvError::Disconnected) => return, Err(TryRecvError::Disconnected) => return,
} }
thread::sleep(Duration::from_micros(100)); thread::sleep(Duration::from_micros(100));
} }
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string())); let _ = tx.send(String::from_utf8_lossy(&buffer).to_string());
} }
} }

View file

@ -1,5 +1,4 @@
use argparse::{ArgumentParser, Store}; use dotenv::{dotenv, vars};
use dirs::config_local_dir;
use eyre::Result; use eyre::Result;
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
use std::collections::HashMap; use std::collections::HashMap;
@ -8,7 +7,6 @@ use std::net::{Shutdown, TcpStream};
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use toml::Table;
mod client_handler; mod client_handler;
mod encryption; mod encryption;
@ -18,61 +16,29 @@ mod server_handler;
mod writer_client; mod writer_client;
fn main() -> Result<()> { fn main() -> Result<()> {
let config_file = config_local_dir() dotenv().expect("Couldn't load .env. It probably doesn't exist");
.expect("Couldn't get config directory") let mut vars_hashmap = HashMap::new();
.join("e2e-irc/config.toml");
if !config_file.exists() { for var in vars() {
panic!("Create a config file at {}", config_file.display()); vars_hashmap.insert(var.0, var.1);
} }
let parsed_config = String::from_utf8_lossy(&fs::read(config_file)?).parse::<Table>()?; let server = &vars_hashmap["SERVER"];
let public_key_location = parsed_config let default_passwd = String::new();
.get("public_key")
.expect("Coudln't get public_key. Try creating it in the config")
.as_str()
.expect("Couldn't convert public_key to str");
let secret_key_location = parsed_config
.get("secret_key")
.expect("Coudln't get secret_key. Try creating it in the config")
.as_str()
.expect("Couldn't convert secret_key to str");
let default_password = toml::Value::String(String::new()); let port = match vars_hashmap.get("PORT") {
Some(val) => val,
let passwd = parsed_config None => "6666",
.get("passwd")
.unwrap_or(&default_password)
.as_str()
.expect("Coudln't convert passwd to str");
let mut server = "irc.vanten-s.com".to_string();
let mut port = "6666".to_string();
let mut server_port = "6697".to_string();
{
let mut ap = ArgumentParser::new();
ap.set_description("Encrypted IRC Bouncer");
ap.refer(&mut server)
.add_argument("server", Store, "The Address Of The Server The Bouncer Connects To")
.required();
ap.refer(&mut port)
.add_option(&["-p", "--port"], Store, "The Port The Bouncer Binds To");
ap.refer(&mut server_port).add_option(
&["--sp", "--server-port"],
Store,
"The TLS Enabled Port Of The Server",
);
ap.parse_args_or_exit();
} }
.to_string();
let server = &server; let passwd = vars_hashmap.get("PASSWD").unwrap_or(&default_passwd);
let stream = TcpStream::connect(format!("{server}:{server_port}"))?; let stream = TcpStream::connect(format!("{server}:6697"))?;
let public_key = fs::read(public_key_location)?; let public_key = fs::read("public.gpg")?;
let secret_key = SignedSecretKey::from_bytes(fs::read(secret_key_location)?.as_slice())?; let secret_key = SignedSecretKey::from_bytes(fs::read("secret.gpg")?.as_slice())?;
let reader_stream = match stream.try_clone() { let reader_stream = match stream.try_clone() {
Ok(stream) => stream, Ok(stream) => stream,