/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::collections::HashMap; use crypto_box::{ aead::{Aead, AeadCore}, SalsaBox, }; use rand::rngs::OsRng; use crate::{ settings::{ChannelInfo, ContactInfo}, Privmsg, }; /// The format we're using is nonce+ciphertext, where nonce is 24 bytes. fn try_decrypt(salt_box: &SalsaBox, ciphertext: &str) -> Option { let bytes = match bs58::decode(ciphertext).into_vec() { Ok(v) => v, Err(_) => return None, }; if bytes.len() < 25 { return None } // Try extracting the nonce let nonce = match bytes[0..24].try_into() { Ok(v) => v, Err(_) => return None, }; // Take the remaining ciphertext let message = &bytes[24..]; // Try decrypting the message match salt_box.decrypt(nonce, message) { Ok(v) => Some(String::from_utf8_lossy(&v).to_string()), Err(_) => None, } } /// The format we're using is nonce+ciphertext, where nonce is 24 bytes. pub fn encrypt(salt_box: &SalsaBox, plaintext: &str) -> String { let nonce = SalsaBox::generate_nonce(&mut OsRng); let mut ciphertext = salt_box.encrypt(&nonce, plaintext.as_bytes()).unwrap(); let mut concat = vec![]; concat.append(&mut nonce.as_slice().to_vec()); concat.append(&mut ciphertext); bs58::encode(concat).into_string() } /// Decrypt PrivMsg target pub fn decrypt_target( contact: &mut String, privmsg: &mut Privmsg, configured_chans: HashMap, configured_contacts: HashMap, ) { for chan_name in configured_chans.keys() { let chan_info = configured_chans.get(chan_name).unwrap(); if !chan_info.joined { continue } let salt_box = chan_info.salt_box.clone(); if let Some(salt_box) = salt_box { let decrypted_target = try_decrypt(&salt_box, &privmsg.target); if decrypted_target.is_none() { continue } let target = decrypted_target.unwrap(); if *chan_name == target { privmsg.target = target; return } } } for cnt_name in configured_contacts.keys() { let cnt_info = configured_contacts.get(cnt_name).unwrap(); let salt_box = cnt_info.salt_box.clone(); if let Some(salt_box) = salt_box { let decrypted_target = try_decrypt(&salt_box, &privmsg.target); if decrypted_target.is_none() { continue } let target = decrypted_target.unwrap(); privmsg.target = target; *contact = cnt_name.into(); return } } } /// Decrypt PrivMsg nickname and message pub fn decrypt_privmsg(salt_box: &SalsaBox, privmsg: &mut Privmsg) { let decrypted_nick = try_decrypt(&salt_box.clone(), &privmsg.nickname); let decrypted_msg = try_decrypt(&salt_box.clone(), &privmsg.message); if decrypted_nick.is_none() && decrypted_msg.is_none() { return } privmsg.nickname = decrypted_nick.unwrap(); privmsg.message = decrypted_msg.unwrap(); } /// Encrypt PrivMsg pub fn encrypt_privmsg(salt_box: &SalsaBox, privmsg: &mut Privmsg) { privmsg.nickname = encrypt(salt_box, &privmsg.nickname); privmsg.target = encrypt(salt_box, &privmsg.target); privmsg.message = encrypt(salt_box, &privmsg.message); }