mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
ircd: Implement e2e message encryption.
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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<String>,
|
||||
configured_chans: FxHashMap<String, ChannelInfo>,
|
||||
configured_contacts: FxHashMap<String, crypto_box::Box>,
|
||||
// p2p
|
||||
p2p: net::P2pPtr,
|
||||
senders: SubscriberPtr<Privmsg>,
|
||||
@@ -64,10 +68,19 @@ impl Ircd {
|
||||
privmsgs_buffer: PrivmsgsBuffer,
|
||||
autojoin_chans: Vec<String>,
|
||||
configured_chans: FxHashMap<String, ChannelInfo>,
|
||||
configured_contacts: FxHashMap<String, crypto_box::Box>,
|
||||
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<Executor<'_>>, p2p_receiver: Receiver<Privmsg>) {
|
||||
@@ -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<Executor<'_>>) -> 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<Executor<'_>>) -> Result<()> {
|
||||
privmsgs_buffer.clone(),
|
||||
settings.autojoin.clone(),
|
||||
configured_chans.clone(),
|
||||
configured_contacts.clone(),
|
||||
p2p.clone(),
|
||||
);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ pub struct IrcServerConnection<C: AsyncRead + AsyncWrite + Send + Unpin + 'stati
|
||||
nickname: String,
|
||||
auto_channels: Vec<String>,
|
||||
pub configured_chans: FxHashMap<String, ChannelInfo>,
|
||||
pub configured_contacts: FxHashMap<String, crypto_box::Box>,
|
||||
capabilities: FxHashMap<String, bool>,
|
||||
// p2p
|
||||
p2p: P2pPtr,
|
||||
@@ -50,6 +51,7 @@ impl<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcServerConnection<C>
|
||||
privmsgs_buffer: PrivmsgsBuffer,
|
||||
auto_channels: Vec<String>,
|
||||
configured_chans: FxHashMap<String, ChannelInfo>,
|
||||
configured_contacts: FxHashMap<String, crypto_box::Box>,
|
||||
p2p: P2pPtr,
|
||||
senders: SubscriberPtr<Privmsg>,
|
||||
subscriber_id: u64,
|
||||
@@ -68,6 +70,7 @@ impl<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcServerConnection<C>
|
||||
nickname: "anon".to_string(),
|
||||
auto_channels,
|
||||
configured_chans,
|
||||
configured_contacts,
|
||||
capabilities,
|
||||
p2p,
|
||||
senders,
|
||||
@@ -200,6 +203,12 @@ impl<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcServerConnection<C>
|
||||
} 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<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcServerConnection<C>
|
||||
|
||||
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(())
|
||||
|
||||
@@ -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<crypto_box::Box> {
|
||||
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<FxHashMap<String, ChannelInfo>> {
|
||||
let toml_contents = std::fs::read_to_string(config_file)?;
|
||||
///
|
||||
/// ```toml
|
||||
/// [contact."7CkVuFgwTUpJn5Sv67Q3fyEDpa28yrSeL5Hg2GqQ4jfM"]
|
||||
/// nicks = ["sneed", "chuck"]
|
||||
/// ```
|
||||
pub fn parse_configured_contacts(data: &str) -> Result<FxHashMap<String, crypto_box::Box>> {
|
||||
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<FxHashMap<String, ChannelInfo>> {
|
||||
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<FxHashMap<Stri
|
||||
|
||||
if chan.1.as_table().unwrap().contains_key("secret") {
|
||||
// Build the NaCl box
|
||||
let s = chan.1["secret"].as_str().unwrap();
|
||||
let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap();
|
||||
let secret = crypto_box::SecretKey::from(bytes);
|
||||
let public = secret.public_key();
|
||||
let msg_box = crypto_box::Box::new(&public, &secret);
|
||||
channel_info.salt_box = Some(msg_box);
|
||||
info!("Instantiated NaCl box for channel {}", chan.0);
|
||||
if let Some(s) = chan.1["secret"].as_str() {
|
||||
let salt_box = salt_box_from_shared_secret(s)?;
|
||||
channel_info.salt_box = Some(salt_box);
|
||||
info!("Instantiated NaCl box for channel {}", chan.0);
|
||||
}
|
||||
}
|
||||
|
||||
ret.insert(chan.0.to_string(), channel_info);
|
||||
|
||||
Reference in New Issue
Block a user