From 5d839844d5217301e5cd0d18b89fa2a22d8c825d Mon Sep 17 00:00:00 2001 From: parazyd Date: Sat, 23 Jul 2022 19:09:56 +0200 Subject: [PATCH] ircd: Implement e2e message encryption. --- bin/ircd/ircd_config.toml | 8 +++++ bin/ircd/src/main.rs | 23 ++++++++++++-- bin/ircd/src/server.rs | 29 +++++++++++++---- bin/ircd/src/settings.rs | 66 +++++++++++++++++++++++++++++++-------- 4 files changed, 104 insertions(+), 22 deletions(-) diff --git a/bin/ircd/ircd_config.toml b/bin/ircd/ircd_config.toml index 8cd05b570..c32816f5f 100644 --- a/bin/ircd/ircd_config.toml +++ b/bin/ircd/ircd_config.toml @@ -47,3 +47,11 @@ seeds=["tls://irc0.dark.fi:11001", "tls://irc1.dark.fi:11001"] #secret = "7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM" ## Topic to set for the channel #topic = "DarkFi Development HQ" + +## Contacts list +# Shared secrets that encrypt direct communication between two nicknames on +# the network. +# These are in the form of secret:[nick0,nick1], which means that the same +# shared secret will be used for all the nicknames in the list. +[contact."7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM"] +nicks = ["sneed", "chuck"] diff --git a/bin/ircd/src/main.rs b/bin/ircd/src/main.rs index 124b615e1..a21e1b218 100644 --- a/bin/ircd/src/main.rs +++ b/bin/ircd/src/main.rs @@ -38,7 +38,10 @@ use crate::{ protocol_privmsg::ProtocolPrivmsg, rpc::JsonRpcInterface, server::IrcServerConnection, - settings::{parse_configured_channels, Args, ChannelInfo, CONFIG_FILE, CONFIG_FILE_CONTENTS}, + settings::{ + parse_configured_channels, parse_configured_contacts, Args, ChannelInfo, CONFIG_FILE, + CONFIG_FILE_CONTENTS, + }, }; const SIZE_OF_MSG_IDSS_BUFFER: usize = 65536; @@ -53,6 +56,7 @@ struct Ircd { // channels autojoin_chans: Vec, configured_chans: FxHashMap, + configured_contacts: FxHashMap, // p2p p2p: net::P2pPtr, senders: SubscriberPtr, @@ -64,10 +68,19 @@ impl Ircd { privmsgs_buffer: PrivmsgsBuffer, autojoin_chans: Vec, configured_chans: FxHashMap, + configured_contacts: FxHashMap, p2p: net::P2pPtr, ) -> Self { let senders = Subscriber::new(); - Self { seen_msg_ids, privmsgs_buffer, autojoin_chans, configured_chans, p2p, senders } + Self { + seen_msg_ids, + privmsgs_buffer, + autojoin_chans, + configured_chans, + configured_contacts, + p2p, + senders, + } } fn start_p2p_receive_loop(&self, executor: Arc>, p2p_receiver: Receiver) { @@ -102,6 +115,7 @@ impl Ircd { self.privmsgs_buffer.clone(), self.autojoin_chans.clone(), self.configured_chans.clone(), + self.configured_contacts.clone(), self.p2p.clone(), self.senders.clone(), receiver.get_id(), @@ -162,7 +176,9 @@ async fn realmain(settings: Args, executor: Arc>) -> Result<()> { // Pick up channel settings from the TOML configuration let cfg_path = get_config_path(settings.config, CONFIG_FILE)?; - let configured_chans = parse_configured_channels(&cfg_path)?; + let toml_contents = std::fs::read_to_string(cfg_path)?; + let configured_chans = parse_configured_channels(&toml_contents)?; + let configured_contacts = parse_configured_contacts(&toml_contents)?; // // P2p setup @@ -256,6 +272,7 @@ async fn realmain(settings: Args, executor: Arc>) -> Result<()> { privmsgs_buffer.clone(), settings.autojoin.clone(), configured_chans.clone(), + configured_contacts.clone(), p2p.clone(), ); diff --git a/bin/ircd/src/server.rs b/bin/ircd/src/server.rs index 1b600e049..4865d017e 100644 --- a/bin/ircd/src/server.rs +++ b/bin/ircd/src/server.rs @@ -34,6 +34,7 @@ pub struct IrcServerConnection, pub configured_chans: FxHashMap, + pub configured_contacts: FxHashMap, capabilities: FxHashMap, // p2p p2p: P2pPtr, @@ -50,6 +51,7 @@ impl IrcServerConnection privmsgs_buffer: PrivmsgsBuffer, auto_channels: Vec, configured_chans: FxHashMap, + configured_contacts: FxHashMap, p2p: P2pPtr, senders: SubscriberPtr, subscriber_id: u64, @@ -68,6 +70,7 @@ impl IrcServerConnection nickname: "anon".to_string(), auto_channels, configured_chans, + configured_contacts, capabilities, p2p, senders, @@ -200,6 +203,12 @@ impl IrcServerConnection } else { message.to_string() }; + } else { + // If we have a configured secret for this nick, we encrypt the message. + if let Some(salt_box) = self.configured_contacts.get(target) { + message = encrypt_message(salt_box, &message); + info!("(Encrypted) PRIVMSG {} :{}", target, message); + } } self.on_receive_privmsg(&message, target).await?; @@ -446,13 +455,21 @@ impl IrcServerConnection self.reply(&msg.to_irc_msg()).await?; return Ok(()) - } + } else { + if self.is_cap_end && + self.is_nick_init && + (self.nickname == msg.target || self.nickname == msg.nickname) + { + if self.configured_contacts.contains_key(&msg.target) { + let salt_box = self.configured_contacts.get(&msg.target).unwrap(); + if let Some(decrypted) = try_decrypt_message(&salt_box, &msg.message) { + msg.message = decrypted; + info!("Decrypted received message: {:?}", msg); + } + } - if self.is_cap_end && - self.is_nick_init && - (self.nickname == msg.target || self.nickname == msg.nickname) - { - self.reply(&msg.to_irc_msg()).await?; + self.reply(&msg.to_irc_msg()).await?; + } } Ok(()) diff --git a/bin/ircd/src/settings.rs b/bin/ircd/src/settings.rs index 17cc33a4e..791924f3d 100644 --- a/bin/ircd/src/settings.rs +++ b/bin/ircd/src/settings.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use fxhash::FxHashMap; use log::info; use serde::Deserialize; @@ -83,13 +81,57 @@ impl ChannelInfo { } } -/// Parse the configuration file for any configured channels and return +fn salt_box_from_shared_secret(s: &str) -> Result { + let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap(); + let secret = crypto_box::SecretKey::from(bytes); + let public = secret.public_key(); + Ok(crypto_box::Box::new(&public, &secret)) +} + +/// Parse a TOML string for any configured contact list and return /// a map containing said configurations. -pub fn parse_configured_channels(config_file: &PathBuf) -> Result> { - let toml_contents = std::fs::read_to_string(config_file)?; +/// +/// ```toml +/// [contact."7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM"] +/// nicks = ["sneed", "chuck"] +/// ``` +pub fn parse_configured_contacts(data: &str) -> Result> { let mut ret = FxHashMap::default(); - if let Value::Table(map) = toml::from_str(&toml_contents)? { + if let Value::Table(map) = toml::from_str(data)? { + if map.contains_key("contact") && map["contact"].is_table() { + for contact in map["contact"].as_table().unwrap() { + // (secret, nicks = [nick0, nick1]) + if contact.1.as_table().unwrap().contains_key("nicks") { + if let Some(nicks) = contact.1["nicks"].as_array() { + let salt_box = salt_box_from_shared_secret(contact.0.as_str())?; + for nick in nicks { + if let Some(n) = nick.as_str() { + info!("Instantiated salt box for {}", n); + ret.insert(n.to_string(), salt_box.clone()); + } + } + } + } + } + } + } + + Ok(ret) +} + +/// Parse a TOML string for any configured channels and return +/// a map containing said configurations. +/// +/// ```toml +/// [channel."#memes"] +/// secret = "7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM" +/// topic = "Dank Memes" +/// ``` +pub fn parse_configured_channels(data: &str) -> Result> { + let mut ret = FxHashMap::default(); + + if let Value::Table(map) = toml::from_str(data)? { if map.contains_key("channel") && map["channel"].is_table() { for chan in map["channel"].as_table().unwrap() { info!("Found configuration for channel {}", chan.0); @@ -103,13 +145,11 @@ pub fn parse_configured_channels(config_file: &PathBuf) -> Result