/* 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);
}