Client auth, Encryption module

This commit is contained in:
Alex-Emmet 2026-01-18 21:42:57 +01:00
commit 5b5fbce22f
15 changed files with 2008 additions and 1003 deletions

2
.gitignore vendored
View file

@ -1 +1 @@
target target

View file

@ -1,123 +1,123 @@
use crate::{ use crate::{
communities::{community::Community, interactables::interactable::Interactable}, communities::{community::Community, interactables::interactable::Interactable},
data::communication::{CommunicationType, CommunicationValue}, data::communication::{CommunicationType, CommunicationValue},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use json::JsonValue; use json::JsonValue;
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
pub struct Category { pub struct Category {
id: Uuid, id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
children: Vec<Arc<Box<dyn Interactable>>>, children: Vec<Arc<Box<dyn Interactable>>>,
} }
impl Category { impl Category {
pub fn new() -> Category { pub fn new() -> Category {
Category { Category {
id: Uuid::new_v4(), id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
children: Vec::new(), children: Vec::new(),
} }
} }
pub fn get_child(&self, path: String, name: String) -> Option<Arc<Box<dyn Interactable>>> { pub fn get_child(&self, path: String, name: String) -> Option<Arc<Box<dyn Interactable>>> {
if path.is_empty() { if path.is_empty() {
self.children self.children
.iter() .iter()
.find(|child| child.get_name() == &name) .find(|child| child.get_name() == &name)
.cloned() .cloned()
} else { } else {
let sub_module = path.split("/").next().unwrap(); let sub_module = path.split("/").next().unwrap();
let next = self let next = self
.children .children
.iter() .iter()
.find(|child| child.get_name() == sub_module) .find(|child| child.get_name() == sub_module)
.unwrap(); .unwrap();
if next.get_codec() == "category" { if next.get_codec() == "category" {
let next_cat = next.as_any().downcast_ref::<Category>().unwrap(); let next_cat = next.as_any().downcast_ref::<Category>().unwrap();
next_cat.get_child(path, name) next_cat.get_child(path, name)
} else { } else {
Some(next.clone()) Some(next.clone())
} }
} }
} }
pub fn get_children(&self) -> Vec<Arc<Box<dyn Interactable>>> { pub fn get_children(&self) -> Vec<Arc<Box<dyn Interactable>>> {
self.children.iter().map(|child| child.clone()).collect() self.children.iter().map(|child| child.clone()).collect()
} }
} }
#[async_trait] #[async_trait]
impl Interactable for Category { impl Interactable for Category {
fn get_id(&self) -> &Uuid { fn get_id(&self) -> &Uuid {
&self.id &self.id
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any { fn as_any_mut(&mut self) -> &mut dyn Any {
self self
} }
fn get_codec(&self) -> String { fn get_codec(&self) -> String {
"category".to_string() "category".to_string()
} }
fn set_name(&mut self, name: String) { fn set_name(&mut self, name: String) {
self.name = name; self.name = name;
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {
self.path = path; self.path = path;
} }
fn get_community(&self) -> &Arc<Community> { fn get_community(&self) -> &Arc<Community> {
&self.community &self.community
} }
fn set_community(&mut self, community: Arc<Community>) { fn set_community(&mut self, community: Arc<Community>) {
self.community = community; self.community = community;
} }
fn get_name(&self) -> &String { fn get_name(&self) -> &String {
&self.name &self.name
} }
fn get_path(&self) -> &String { fn get_path(&self) -> &String {
&self.path &self.path
} }
fn get_total_path(&self) -> String { fn get_total_path(&self) -> String {
String::new() + &self.path + "/" + &self.name String::new() + &self.path + "/" + &self.name
} }
fn get_data(&self) -> JsonValue { fn get_data(&self) -> JsonValue {
let mut v = JsonValue::new_object(); let mut v = JsonValue::new_object();
for child in &self.children { for child in &self.children {
let mut subject = JsonValue::new_object(); let mut subject = JsonValue::new_object();
subject["codec"] = JsonValue::String(child.get_codec()); subject["codec"] = JsonValue::String(child.get_codec());
subject["data"] = child.get_data(); subject["data"] = child.get_data();
v[child.get_name()] = subject; v[child.get_name()] = subject;
} }
v v
} }
async fn run_function(&self, _cv: CommunicationValue) -> CommunicationValue { async fn run_function(&self, _cv: CommunicationValue) -> CommunicationValue {
CommunicationValue::new(CommunicationType::error) CommunicationValue::new(CommunicationType::error)
} }
fn to_json(&self) -> JsonValue { fn to_json(&self) -> JsonValue {
let mut v = JsonValue::new_object(); let mut v = JsonValue::new_object();
v["children"] = JsonValue::new_array(); v["children"] = JsonValue::new_array();
for child in &self.children { for child in &self.children {
let _ = v["children"].push(child.to_json()); let _ = v["children"].push(child.to_json());
} }
v v
} }
fn load( fn load(
&mut self, &mut self,
community: Arc<Community>, community: Arc<Community>,
id: Uuid, id: Uuid,
path: String, path: String,
name: String, name: String,
_json: &JsonValue, _json: &JsonValue,
) { ) {
self.community = community; self.community = community;
self.id = id; self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }
} }

View file

@ -1,34 +1,34 @@
use crate::{communities::community::Community, data::communication::CommunicationValue}; use crate::{communities::community::Community, data::communication::CommunicationValue};
use async_trait::async_trait; use async_trait::async_trait;
use json::JsonValue; use json::JsonValue;
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
pub type InteractableFactory = fn() -> Box<dyn Interactable>; pub type InteractableFactory = fn() -> Box<dyn Interactable>;
#[async_trait] #[async_trait]
pub trait Interactable: Send + Sync + Any { pub trait Interactable: Send + Sync + Any {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any;
fn get_codec(&self) -> String; fn get_codec(&self) -> String;
fn get_name(&self) -> &String; fn get_name(&self) -> &String;
fn get_path(&self) -> &String; fn get_path(&self) -> &String;
fn get_total_path(&self) -> String; fn get_total_path(&self) -> String;
fn set_name(&mut self, name: String); fn set_name(&mut self, name: String);
fn set_path(&mut self, path: String); fn set_path(&mut self, path: String);
fn get_community(&self) -> &Arc<Community>; fn get_community(&self) -> &Arc<Community>;
fn set_community(&mut self, community: Arc<Community>); fn set_community(&mut self, community: Arc<Community>);
async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue; async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue;
fn get_data(&self) -> JsonValue; fn get_data(&self) -> JsonValue;
fn get_id(&self) -> &Uuid; fn get_id(&self) -> &Uuid;
fn to_json(&self) -> JsonValue; fn to_json(&self) -> JsonValue;
fn load( fn load(
&mut self, &mut self,
community: Arc<Community>, community: Arc<Community>,
id: Uuid, id: Uuid,
path: String, path: String,
name: String, name: String,
json: &JsonValue, json: &JsonValue,
); );
} }

View file

@ -1,75 +1,75 @@
use crate::communities::community::Community; use crate::communities::community::Community;
use crate::communities::interactables::category::Category; use crate::communities::interactables::category::Category;
use crate::communities::interactables::interactable::{Interactable, InteractableFactory}; use crate::communities::interactables::interactable::{Interactable, InteractableFactory};
use crate::communities::interactables::text_chat::TextChat; use crate::communities::interactables::text_chat::TextChat;
use crate::communities::interactables::voice_chat::VoiceChat; use crate::communities::interactables::voice_chat::VoiceChat;
use crate::util::file_util; use crate::util::file_util;
use json::JsonValue; use json::JsonValue;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use uuid::Uuid; use uuid::Uuid;
pub static INTERACTABLE_REGISTRY: Lazy<Arc<Mutex<HashMap<String, InteractableFactory>>>> = pub static INTERACTABLE_REGISTRY: Lazy<Arc<Mutex<HashMap<String, InteractableFactory>>>> =
Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
pub async fn load_interactables() { pub async fn load_interactables() {
INTERACTABLE_REGISTRY INTERACTABLE_REGISTRY
.lock() .lock()
.await .await
.insert(TextChat::new().get_codec(), || { .insert(TextChat::new().get_codec(), || {
Box::new(TextChat::new()) as Box<dyn Interactable> Box::new(TextChat::new()) as Box<dyn Interactable>
}); });
INTERACTABLE_REGISTRY INTERACTABLE_REGISTRY
.lock() .lock()
.await .await
.insert(VoiceChat::new().get_codec(), || { .insert(VoiceChat::new().get_codec(), || {
Box::new(VoiceChat::new()) as Box<dyn Interactable> Box::new(VoiceChat::new()) as Box<dyn Interactable>
}); });
INTERACTABLE_REGISTRY INTERACTABLE_REGISTRY
.lock() .lock()
.await .await
.insert(Category::new().get_codec(), || { .insert(Category::new().get_codec(), || {
Box::new(Category::new()) as Box<dyn Interactable> Box::new(Category::new()) as Box<dyn Interactable>
}); });
} }
pub async fn register_interactable(name: String, interactable: InteractableFactory) { pub async fn register_interactable(name: String, interactable: InteractableFactory) {
INTERACTABLE_REGISTRY INTERACTABLE_REGISTRY
.lock() .lock()
.await .await
.insert(name.to_string(), interactable) .insert(name.to_string(), interactable)
.unwrap(); .unwrap();
} }
pub async fn get_interactable(name: &str) -> Box<dyn Interactable> { pub async fn get_interactable(name: &str) -> Box<dyn Interactable> {
INTERACTABLE_REGISTRY.lock().await.get(name).unwrap()() INTERACTABLE_REGISTRY.lock().await.get(name).unwrap()()
} }
pub async fn save(interactable: &Arc<Box<dyn Interactable>>) { pub async fn save(interactable: &Arc<Box<dyn Interactable>>) {
let mut json_object: JsonValue = interactable.to_json().clone(); let mut json_object: JsonValue = interactable.to_json().clone();
json_object["codec"] = JsonValue::String(interactable.get_codec()); json_object["codec"] = JsonValue::String(interactable.get_codec());
json_object["id"] = JsonValue::String(interactable.get_id().to_string()); json_object["id"] = JsonValue::String(interactable.get_id().to_string());
file_util::save_file( file_util::save_file(
&format!( &format!(
"communities/{}/interactables/{}", "communities/{}/interactables/{}",
interactable.get_community().get_name(), interactable.get_community().get_name(),
interactable.get_path() interactable.get_path()
), ),
&format!("{}.json", interactable.get_name()), &format!("{}.json", interactable.get_name()),
&json_object.to_string(), &json_object.to_string(),
); );
} }
pub async fn load( pub async fn load(
c: Arc<Community>, c: Arc<Community>,
path: String, path: String,
name: String, name: String,
) -> Box<dyn Interactable + 'static> { ) -> Box<dyn Interactable + 'static> {
let s = file_util::load_file( let s = file_util::load_file(
&format!("communities/{}/interactables/{}", c.get_name(), path), &format!("communities/{}/interactables/{}", c.get_name(), path),
&format!("{}.json", name), &format!("{}.json", name),
); );
let json_object: JsonValue = json::parse(&s).unwrap(); let json_object: JsonValue = json::parse(&s).unwrap();
let codec: String = json_object["codec"].as_str().unwrap().to_string(); let codec: String = json_object["codec"].as_str().unwrap().to_string();
let id: String = json_object["id"].as_str().unwrap().to_string(); let id: String = json_object["id"].as_str().unwrap().to_string();
let mut interactable = get_interactable(&codec).await; let mut interactable = get_interactable(&codec).await;
interactable.load(c, Uuid::parse_str(&id).unwrap(), path, name, &json_object); interactable.load(c, Uuid::parse_str(&id).unwrap(), path, name, &json_object);
interactable interactable
} }

View file

@ -1,262 +1,262 @@
use crate::{ use crate::{
communities::{ communities::{
community::Community, community_connection::CommunityConnection, community::Community, community_connection::CommunityConnection,
interactables::interactable::Interactable, interactables::interactable::Interactable,
}, },
data::communication::{CommunicationType, CommunicationValue, DataTypes}, data::communication::{CommunicationType, CommunicationValue, DataTypes},
gui::log_panel::log_message, gui::log_panel::log_message,
util::file_util::{get_children, load_file, save_file}, util::file_util::{get_children, load_file, save_file},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use json::{JsonValue, array, object}; use json::{JsonValue, array, object};
use std::fs; use std::fs;
use std::sync::Arc; use std::sync::Arc;
use std::{any::Any, collections::HashMap}; use std::{any::Any, collections::HashMap};
use uuid::Uuid; use uuid::Uuid;
pub struct TextChat { pub struct TextChat {
id: Uuid, id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
} }
impl TextChat { impl TextChat {
pub fn new() -> TextChat { pub fn new() -> TextChat {
TextChat { TextChat {
id: Uuid::new_v4(), id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
} }
} }
pub fn add_message(&self, send_time: u128, sender: i64, message: &str) { pub fn add_message(&self, send_time: u128, sender: i64, message: &str) {
let user_dir = &format!( let user_dir = &format!(
"communities/{}/interactables/{}/{}", "communities/{}/interactables/{}/{}",
self.get_community().get_name(), self.get_community().get_name(),
self.get_path(), self.get_path(),
self.get_name() self.get_name()
); );
if let Err(e) = fs::create_dir_all(user_dir) { if let Err(e) = fs::create_dir_all(user_dir) {
log_message(format!("Failed to create chat directory: {}", e)); log_message(format!("Failed to create chat directory: {}", e));
return; return;
} }
let mut chunk_index = 0; let mut chunk_index = 0;
let mut message_chunk = array![]; let mut message_chunk = array![];
// find latest chunk not full (max 800 msgs) // find latest chunk not full (max 800 msgs)
loop { loop {
let file_name = format!("msgs_{}.json", chunk_index); let file_name = format!("msgs_{}.json", chunk_index);
let file_content = load_file(&user_dir, &file_name); let file_content = load_file(&user_dir, &file_name);
if !file_content.is_empty() { if !file_content.is_empty() {
if let Ok(current_chunk) = json::parse(&file_content) { if let Ok(current_chunk) = json::parse(&file_content) {
if current_chunk.is_array() && current_chunk.len() < 800 { if current_chunk.is_array() && current_chunk.len() < 800 {
message_chunk = current_chunk; message_chunk = current_chunk;
break; break;
} }
} else { } else {
log_message(format!("Failed to parse existing JSON file: {}", file_name)); log_message(format!("Failed to parse existing JSON file: {}", file_name));
} }
} else { } else {
// New file, use empty array // New file, use empty array
break; break;
} }
chunk_index += 1; chunk_index += 1;
if chunk_index > 1000 { if chunk_index > 1000 {
log_message(format!("Too many message chunks. Aborting add.")); log_message(format!("Too many message chunks. Aborting add."));
return; return;
} }
} }
let json_obj = object! { let json_obj = object! {
"timestamp" => send_time as i64, "timestamp" => send_time as i64,
"content" => message, "content" => message,
"sender" => sender.to_string(), "sender" => sender.to_string(),
}; };
if let Err(e) = message_chunk.push(json_obj) { if let Err(e) = message_chunk.push(json_obj) {
log_message(format!("Failed to push new message into JSON array: {}", e)); log_message(format!("Failed to push new message into JSON array: {}", e));
return; return;
} }
let file_name = format!("msgs_{}.json", chunk_index); let file_name = format!("msgs_{}.json", chunk_index);
log_message(format!("Saving message to {}/{}", user_dir, file_name)); log_message(format!("Saving message to {}/{}", user_dir, file_name));
save_file(&user_dir, &file_name, &message_chunk.dump()); save_file(&user_dir, &file_name, &message_chunk.dump());
} }
pub fn get_messages(&self, loaded_messages: i64, amount: i64) -> JsonValue { pub fn get_messages(&self, loaded_messages: i64, amount: i64) -> JsonValue {
let mut messages = array![]; let mut messages = array![];
let mut latest_chunk_index: i32 = -1; let mut latest_chunk_index: i32 = -1;
let files = get_children(&format!( let files = get_children(&format!(
"communities/{}/interactables/{}/{}", "communities/{}/interactables/{}/{}",
self.get_community().get_name(), self.get_community().get_name(),
self.get_path(), self.get_path(),
self.get_name() self.get_name()
)); ));
for entry in files { for entry in files {
if let Some(num) = { if let Some(num) = {
entry entry
.strip_prefix("msgs_") .strip_prefix("msgs_")
.and_then(|s| s.strip_suffix(".json")) .and_then(|s| s.strip_suffix(".json"))
} { } {
if let Ok(index) = num.parse::<i32>() { if let Ok(index) = num.parse::<i32>() {
if index > latest_chunk_index { if index > latest_chunk_index {
latest_chunk_index = index; latest_chunk_index = index;
} }
} }
} }
} }
if latest_chunk_index == -1 { if latest_chunk_index == -1 {
return messages; return messages;
} }
let mut to_skip = loaded_messages; let mut to_skip = loaded_messages;
let mut needed = amount; let mut needed = amount;
for chunk_index in (0..=latest_chunk_index).rev() { for chunk_index in (0..=latest_chunk_index).rev() {
if needed == 0 { if needed == 0 {
break; break;
} }
let file_name = format!("msgs_{}.json", chunk_index); let file_name = format!("msgs_{}.json", chunk_index);
let file_content = load_file( let file_content = load_file(
&format!( &format!(
"communities/{}/interactables/{}/{}", "communities/{}/interactables/{}/{}",
self.get_community().get_name(), self.get_community().get_name(),
self.get_path(), self.get_path(),
self.get_name() self.get_name()
), ),
&file_name, &file_name,
); );
if file_content.is_empty() { if file_content.is_empty() {
continue; continue;
} }
if let Ok(chunk) = json::parse(&file_content) { if let Ok(chunk) = json::parse(&file_content) {
for i in (0..chunk.len()).rev() { for i in (0..chunk.len()).rev() {
if needed == 0 { if needed == 0 {
break; break;
} }
if to_skip > 0 { if to_skip > 0 {
to_skip -= 1; to_skip -= 1;
continue; continue;
} }
messages.push(chunk[i].clone()).unwrap(); messages.push(chunk[i].clone()).unwrap();
needed -= 1; needed -= 1;
} }
} }
} }
messages messages
} }
} }
#[async_trait] #[async_trait]
impl Interactable for TextChat { impl Interactable for TextChat {
fn get_id(&self) -> &Uuid { fn get_id(&self) -> &Uuid {
&self.id &self.id
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any { fn as_any_mut(&mut self) -> &mut dyn Any {
self self
} }
fn get_codec(&self) -> String { fn get_codec(&self) -> String {
"text".to_string() "text".to_string()
} }
fn set_name(&mut self, name: String) { fn set_name(&mut self, name: String) {
self.name = name; self.name = name;
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {
self.path = path; self.path = path;
} }
fn get_community(&self) -> &Arc<Community> { fn get_community(&self) -> &Arc<Community> {
&self.community &self.community
} }
fn set_community(&mut self, community: Arc<Community>) { fn set_community(&mut self, community: Arc<Community>) {
self.community = community; self.community = community;
} }
fn get_name(&self) -> &String { fn get_name(&self) -> &String {
&self.name &self.name
} }
fn get_path(&self) -> &String { fn get_path(&self) -> &String {
&self.path &self.path
} }
fn get_total_path(&self) -> String { fn get_total_path(&self) -> String {
String::new() + &self.path + "/" + &self.name String::new() + &self.path + "/" + &self.name
} }
fn get_data(&self) -> JsonValue { fn get_data(&self) -> JsonValue {
JsonValue::new_object() JsonValue::new_object()
} }
async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue { async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue {
let payload = cv.get_data(DataTypes::payload).unwrap(); let payload = cv.get_data(DataTypes::payload).unwrap();
if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "get_messages" { if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "get_messages" {
let amount = payload["amount"].as_i64().unwrap(); let amount = payload["amount"].as_i64().unwrap();
let loaded_messages = payload["loaded_messages"].as_i64().unwrap(); let loaded_messages = payload["loaded_messages"].as_i64().unwrap();
let messages = self.get_messages(loaded_messages, amount).clone(); let messages = self.get_messages(loaded_messages, amount).clone();
let mut payload = JsonValue::new_object(); let mut payload = JsonValue::new_object();
payload["messages"] = messages; payload["messages"] = messages;
return CommunicationValue::new(CommunicationType::function) return CommunicationValue::new(CommunicationType::function)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_data_str(DataTypes::name, self.name.clone()) .add_data_str(DataTypes::name, self.name.clone())
.add_data_str(DataTypes::path, self.path.clone()) .add_data_str(DataTypes::path, self.path.clone())
.add_data_str(DataTypes::result, "message_chunk".to_string()) .add_data_str(DataTypes::result, "message_chunk".to_string())
.add_data(DataTypes::payload, payload); .add_data(DataTypes::payload, payload);
} }
if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "send_message" { if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "send_message" {
let message = payload["message"].as_str().unwrap(); let message = payload["message"].as_str().unwrap();
let milliseconds_timestamp: u128 = std::time::SystemTime::now() let milliseconds_timestamp: u128 = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.unwrap() .unwrap()
.as_millis(); .as_millis();
self.add_message(milliseconds_timestamp, cv.get_sender(), message); self.add_message(milliseconds_timestamp, cv.get_sender(), message);
let mut distribution_payload = JsonValue::new_object(); let mut distribution_payload = JsonValue::new_object();
distribution_payload["message"] = JsonValue::String(message.to_string()); distribution_payload["message"] = JsonValue::String(message.to_string());
distribution_payload["sender_id"] = JsonValue::String(cv.get_sender().to_string()); distribution_payload["sender_id"] = JsonValue::String(cv.get_sender().to_string());
distribution_payload["send_time"] = distribution_payload["send_time"] =
JsonValue::String(milliseconds_timestamp.to_string()); JsonValue::String(milliseconds_timestamp.to_string());
let distribution = CommunicationValue::new(CommunicationType::update) let distribution = CommunicationValue::new(CommunicationType::update)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_data_str(DataTypes::name, self.name.clone()) .add_data_str(DataTypes::name, self.name.clone())
.add_data_str(DataTypes::path, self.path.clone()) .add_data_str(DataTypes::path, self.path.clone())
.add_data_str(DataTypes::result, "message_live".to_string()) .add_data_str(DataTypes::result, "message_live".to_string())
.add_data(DataTypes::payload, distribution_payload); .add_data(DataTypes::payload, distribution_payload);
let connections: HashMap<i64, Vec<Arc<CommunityConnection>>> = let connections: HashMap<i64, Vec<Arc<CommunityConnection>>> =
self.get_community().get_connections().await.clone(); self.get_community().get_connections().await.clone();
for con in connections.values() { for con in connections.values() {
for c in con { for c in con {
let cd: &Arc<CommunityConnection> = c; let cd: &Arc<CommunityConnection> = c;
cd.send_message(&distribution).await; cd.send_message(&distribution).await;
} }
} }
return CommunicationValue::new(CommunicationType::function) return CommunicationValue::new(CommunicationType::function)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_data_str(DataTypes::name, self.name.clone()) .add_data_str(DataTypes::name, self.name.clone())
.add_data_str(DataTypes::path, self.path.clone()) .add_data_str(DataTypes::path, self.path.clone())
.add_data_str(DataTypes::result, "message_received".to_string()) .add_data_str(DataTypes::result, "message_received".to_string())
.add_data(DataTypes::payload, JsonValue::new_object()); .add_data(DataTypes::payload, JsonValue::new_object());
} }
CommunicationValue::new(CommunicationType::error).with_id(cv.get_id()) CommunicationValue::new(CommunicationType::error).with_id(cv.get_id())
} }
fn to_json(&self) -> JsonValue { fn to_json(&self) -> JsonValue {
JsonValue::new_object() JsonValue::new_object()
} }
fn load( fn load(
&mut self, &mut self,
community: Arc<Community>, community: Arc<Community>,
id: Uuid, id: Uuid,
path: String, path: String,
name: String, name: String,
_json: &JsonValue, _json: &JsonValue,
) { ) {
self.community = community; self.community = community;
self.id = id; self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }
} }

View file

@ -1,190 +1,190 @@
use crate::{ use crate::{
communities::{community::Community, interactables::interactable::Interactable}, communities::{community::Community, interactables::interactable::Interactable},
data::communication::{CommunicationType, CommunicationValue, DataTypes}, data::communication::{CommunicationType, CommunicationValue, DataTypes},
}; };
use async_trait::async_trait; use async_trait::async_trait;
use json::JsonValue; use json::JsonValue;
use std::sync::Arc; use std::sync::Arc;
use std::{any::Any, sync::RwLock}; use std::{any::Any, sync::RwLock};
use uuid::Uuid; use uuid::Uuid;
pub enum CallUserState { pub enum CallUserState {
Active, Active,
Muted, Muted,
Deafed, Deafed,
} }
impl CallUserState { impl CallUserState {
pub fn parse(state: &str) -> CallUserState { pub fn parse(state: &str) -> CallUserState {
match state { match state {
"active" => CallUserState::Active, "active" => CallUserState::Active,
"muted" => CallUserState::Muted, "muted" => CallUserState::Muted,
"deafed" => CallUserState::Deafed, "deafed" => CallUserState::Deafed,
_ => CallUserState::Active, _ => CallUserState::Active,
} }
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match self { match self {
CallUserState::Active => "active".to_string(), CallUserState::Active => "active".to_string(),
CallUserState::Muted => "muted".to_string(), CallUserState::Muted => "muted".to_string(),
CallUserState::Deafed => "deafed".to_string(), CallUserState::Deafed => "deafed".to_string(),
} }
} }
} }
pub struct CallUser { pub struct CallUser {
pub user_id: Uuid, pub user_id: Uuid,
pub user_state: CallUserState, pub user_state: CallUserState,
pub streaming: bool, pub streaming: bool,
} }
pub struct VoiceChat { pub struct VoiceChat {
id: Uuid, id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
users: RwLock<Vec<CallUser>>, users: RwLock<Vec<CallUser>>,
} }
impl VoiceChat { impl VoiceChat {
pub fn new() -> VoiceChat { pub fn new() -> VoiceChat {
VoiceChat { VoiceChat {
id: Uuid::new_v4(), id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
users: RwLock::new(Vec::new()), users: RwLock::new(Vec::new()),
} }
} }
pub fn update_user_state( pub fn update_user_state(
self: Arc<Self>, self: Arc<Self>,
user_id: Uuid, user_id: Uuid,
state: CallUserState, state: CallUserState,
streaming: bool, streaming: bool,
) { ) {
if let Some(user) = self if let Some(user) = self
.users .users
.write() .write()
.unwrap() .unwrap()
.iter_mut() .iter_mut()
.find(|u| u.user_id == user_id) .find(|u| u.user_id == user_id)
{ {
user.user_state = state; user.user_state = state;
user.streaming = streaming; user.streaming = streaming;
} }
} }
} }
#[async_trait] #[async_trait]
impl Interactable for VoiceChat { impl Interactable for VoiceChat {
fn get_id(&self) -> &Uuid { fn get_id(&self) -> &Uuid {
&self.id &self.id
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any { fn as_any_mut(&mut self) -> &mut dyn Any {
self self
} }
fn get_codec(&self) -> String { fn get_codec(&self) -> String {
"voice".to_string() "voice".to_string()
} }
fn set_name(&mut self, name: String) { fn set_name(&mut self, name: String) {
self.name = name; self.name = name;
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {
self.path = path; self.path = path;
} }
fn get_community(&self) -> &Arc<Community> { fn get_community(&self) -> &Arc<Community> {
&self.community &self.community
} }
fn set_community(&mut self, community: Arc<Community>) { fn set_community(&mut self, community: Arc<Community>) {
self.community = community; self.community = community;
} }
fn get_name(&self) -> &String { fn get_name(&self) -> &String {
&self.name &self.name
} }
fn get_path(&self) -> &String { fn get_path(&self) -> &String {
&self.path &self.path
} }
fn get_total_path(&self) -> String { fn get_total_path(&self) -> String {
String::new() + &self.path + "/" + &self.name String::new() + &self.path + "/" + &self.name
} }
fn get_data(&self) -> JsonValue { fn get_data(&self) -> JsonValue {
let mut data = JsonValue::new_object(); let mut data = JsonValue::new_object();
let mut active_users = JsonValue::new_object(); let mut active_users = JsonValue::new_object();
for user in self.users.read().unwrap().iter() { for user in self.users.read().unwrap().iter() {
let mut user_data = JsonValue::new_object(); let mut user_data = JsonValue::new_object();
let _ = user_data.insert("state", JsonValue::String(user.user_state.to_string())); let _ = user_data.insert("state", JsonValue::String(user.user_state.to_string()));
let _ = user_data.insert("streaming", JsonValue::Boolean(user.streaming)); let _ = user_data.insert("streaming", JsonValue::Boolean(user.streaming));
let _ = active_users.insert(&user.user_id.to_string(), user_data); let _ = active_users.insert(&user.user_id.to_string(), user_data);
} }
let _ = data.insert("active_users", active_users); let _ = data.insert("active_users", active_users);
data data
} }
async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue { async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue {
let payload = cv.get_data(DataTypes::payload).unwrap(); let payload = cv.get_data(DataTypes::payload).unwrap();
let function = cv.get_data(DataTypes::function).unwrap().as_str().unwrap(); let function = cv.get_data(DataTypes::function).unwrap().as_str().unwrap();
if function == "get_call" { if function == "get_call" {
let sender_id = payload["sender_id"].as_str().unwrap(); let sender_id = payload["sender_id"].as_str().unwrap();
let message_id = payload["message"].as_str().unwrap(); let message_id = payload["message"].as_str().unwrap();
let send_time = payload["send_time"].as_str().unwrap(); let send_time = payload["send_time"].as_str().unwrap();
let mut response_payload = JsonValue::new_object(); let mut response_payload = JsonValue::new_object();
response_payload["sender_id"] = JsonValue::String(sender_id.to_string()); response_payload["sender_id"] = JsonValue::String(sender_id.to_string());
response_payload["message"] = JsonValue::String(message_id.to_string()); response_payload["message"] = JsonValue::String(message_id.to_string());
response_payload["send_time"] = JsonValue::String(send_time.to_string()); response_payload["send_time"] = JsonValue::String(send_time.to_string());
return CommunicationValue::new(CommunicationType::function) return CommunicationValue::new(CommunicationType::function)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_data_str(DataTypes::name, self.name.clone()) .add_data_str(DataTypes::name, self.name.clone())
.add_data_str(DataTypes::path, self.path.clone()) .add_data_str(DataTypes::path, self.path.clone())
.add_data_str(DataTypes::result, "getting_call".to_string()) .add_data_str(DataTypes::result, "getting_call".to_string())
.add_data(DataTypes::payload, response_payload); .add_data(DataTypes::payload, response_payload);
} }
if function == "update_user_state" { if function == "update_user_state" {
let user_id = payload["user_id"].as_str().unwrap(); let user_id = payload["user_id"].as_str().unwrap();
let state = payload["state"].as_str().unwrap(); let state = payload["state"].as_str().unwrap();
let streaming = payload["streaming"].as_bool().unwrap(); let streaming = payload["streaming"].as_bool().unwrap();
if let Some(user) = self if let Some(user) = self
.users .users
.write() .write()
.unwrap() .unwrap()
.iter_mut() .iter_mut()
.find(|u| u.user_id == Uuid::parse_str(user_id).unwrap()) .find(|u| u.user_id == Uuid::parse_str(user_id).unwrap())
{ {
user.user_state = CallUserState::parse(state); user.user_state = CallUserState::parse(state);
user.streaming = streaming; user.streaming = streaming;
} }
let mut response_payload = JsonValue::new_object(); let mut response_payload = JsonValue::new_object();
response_payload["user_id"] = JsonValue::String(user_id.to_string()); response_payload["user_id"] = JsonValue::String(user_id.to_string());
response_payload["state"] = JsonValue::String(state.to_string()); response_payload["state"] = JsonValue::String(state.to_string());
response_payload["streaming"] = JsonValue::Boolean(streaming); response_payload["streaming"] = JsonValue::Boolean(streaming);
return CommunicationValue::new(CommunicationType::update) return CommunicationValue::new(CommunicationType::update)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_data_str(DataTypes::name, self.name.clone()) .add_data_str(DataTypes::name, self.name.clone())
.add_data_str(DataTypes::path, self.path.clone()) .add_data_str(DataTypes::path, self.path.clone())
.add_data_str(DataTypes::result, "user_changed".to_string()) .add_data_str(DataTypes::result, "user_changed".to_string())
.add_data(DataTypes::payload, response_payload); .add_data(DataTypes::payload, response_payload);
} }
CommunicationValue::new(CommunicationType::error).with_id(cv.get_id()) CommunicationValue::new(CommunicationType::error).with_id(cv.get_id())
} }
fn to_json(&self) -> JsonValue { fn to_json(&self) -> JsonValue {
let v = JsonValue::new_object(); let v = JsonValue::new_object();
v v
} }
fn load( fn load(
&mut self, &mut self,
community: Arc<Community>, community: Arc<Community>,
id: Uuid, id: Uuid,
path: String, path: String,
name: String, name: String,
_json: &JsonValue, _json: &JsonValue,
) { ) {
self.community = community; self.community = community;
self.id = id; self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }
} }

View file

@ -1,13 +1,13 @@
pub mod community_manager; pub mod community_manager;
pub mod interactables { pub mod interactables {
pub mod category; pub mod category;
pub mod interactable; pub mod interactable;
pub mod registry; pub mod registry;
pub mod text_chat; pub mod text_chat;
pub mod voice_chat; pub mod voice_chat;
} }
pub mod community; pub mod community;
pub mod community_connection; pub mod community_connection;
pub mod perms { pub mod perms {
pub mod permission; pub mod permission;
} }

View file

@ -1,27 +1,27 @@
use json::JsonValue; use json::JsonValue;
use uuid::Uuid; use uuid::Uuid;
pub struct Permission { pub struct Permission {
pub id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
} }
impl Permission { impl Permission {
pub fn new(id: Uuid, name: String) -> Self { pub fn new(id: Uuid, name: String) -> Self {
Permission { id, name } Permission { id, name }
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> JsonValue {
json::object! { json::object! {
"id" => self.id.to_string(), "id" => self.id.to_string(),
"name" => self.name.clone() "name" => self.name.clone()
} }
} }
pub fn from_json(json: JsonValue) -> Self { pub fn from_json(json: JsonValue) -> Self {
Permission { Permission {
id: Uuid::parse_str(json["id"].as_str().unwrap()).unwrap(), id: Uuid::parse_str(json["id"].as_str().unwrap()).unwrap(),
name: json["name"].as_str().unwrap().to_string(), name: json["name"].as_str().unwrap().to_string(),
} }
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
self.to_json().as_str().unwrap().to_string() self.to_json().as_str().unwrap().to_string()
} }
} }

View file

@ -250,6 +250,7 @@ pub enum CommunicationType {
get_user_data, get_user_data,
get_iota_data, get_iota_data,
iota_user_data,
change_user_data, change_user_data,
change_iota_data, change_iota_data,
@ -342,6 +343,7 @@ impl CommunicationType {
"getuserdata" => CommunicationType::get_user_data, "getuserdata" => CommunicationType::get_user_data,
"getiotadata" => CommunicationType::get_iota_data, "getiotadata" => CommunicationType::get_iota_data,
"iotauserdata" => CommunicationType::iota_user_data,
"changeuserdata" => CommunicationType::change_user_data, "changeuserdata" => CommunicationType::change_user_data,
"changeiotadata" => CommunicationType::change_iota_data, "changeiotadata" => CommunicationType::change_iota_data,
@ -362,11 +364,11 @@ impl CommunicationType {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CommunicationValue { pub struct CommunicationValue {
pub id: Uuid, id: Uuid,
pub comm_type: CommunicationType, comm_type: CommunicationType,
pub sender: i64, sender: i64,
pub receiver: i64, receiver: i64,
pub data: HashMap<DataTypes, JsonValue>, data: HashMap<DataTypes, JsonValue>,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -421,7 +423,10 @@ impl CommunicationValue {
self.data.get(&key) self.data.get(&key)
} }
pub(crate) fn is_type(&self, p0: CommunicationType) -> bool { pub fn get_type(&self) -> CommunicationType {
self.comm_type.clone()
}
pub fn is_type(&self, p0: CommunicationType) -> bool {
self.comm_type == p0 self.comm_type == p0
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> JsonValue {

View file

@ -1,26 +1,26 @@
use crate::util::file_util::{load_file, save_file}; use crate::util::file_util::{load_file, save_file};
pub fn check_eula() -> bool { pub fn check_eula() -> bool {
let eula = "By changing the value to \"true\" you agree to our end user license agreement and our terms of service!\ let eula = "By changing the value to \"true\" you agree to our end user license agreement and our terms of service!\
\nYou can find our Terms of service on https://docs.tensamin.net/legal/terms-of-service/.\ \nYou can find our Terms of service on https://docs.tensamin.net/legal/terms-of-service/.\
\neula=false"; \neula=false";
let file = load_file("", "eula.txt"); let file = load_file("", "eula.txt");
if file.is_empty() { if file.is_empty() {
save_file("", "eula.txt", eula); save_file("", "eula.txt", eula);
return false; return false;
} }
if file.contains("eula=false") { if file.contains("eula=false") {
false false
} else if file.contains("eula=true") { } else if file.contains("eula=true") {
true true
} else { } else {
false false
} }
} }
pub fn accept_eula() { pub fn accept_eula() {
let eula = "By changing the value to \"true\" you agree to our end user license agreement and our terms of service!\ let eula = "By changing the value to \"true\" you agree to our end user license agreement and our terms of service!\
\nYou can find our Terms of service on https://docs.tensamin.net/legal/terms-of-service/.\ \nYou can find our Terms of service on https://docs.tensamin.net/legal/terms-of-service/.\
\neula=true"; \neula=true";
save_file("", "eula.txt", eula); save_file("", "eula.txt", eula);
} }

View file

@ -1,108 +1,108 @@
use crate::ACTIVE_TASKS; use crate::ACTIVE_TASKS;
use crate::APP_STATE; use crate::APP_STATE;
use crate::SHUTDOWN; use crate::SHUTDOWN;
use crate::gui::tui::UNIQUE; use crate::gui::tui::UNIQUE;
use crate::langu::language_manager::format; use crate::langu::language_manager::format;
use crate::langu::language_manager::from_key; use crate::langu::language_manager::from_key;
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
use std::{thread, time::Duration}; use std::{thread, time::Duration};
use sysinfo::{RefreshKind, System}; use sysinfo::{RefreshKind, System};
pub fn log_cv(cv: &CommunicationValue) { pub fn log_cv(cv: &CommunicationValue) {
if cv.is_type(CommunicationType::identification_response) { if cv.is_type(CommunicationType::identification_response) {
let args = [cv.get_data(DataTypes::accepted).unwrap().as_str().unwrap()]; let args = [cv.get_data(DataTypes::accepted).unwrap().as_str().unwrap()];
log_message(format(&"identification_response", &args)); log_message(format(&"identification_response", &args));
} else { } else {
log_message_trans(format!("{:?}", &cv.comm_type)); log_message_trans(format!("{:?}", &cv.get_type()));
} }
tokio::spawn(async move { tokio::spawn(async move {
*UNIQUE.write().await = true; *UNIQUE.write().await = true;
}); });
} }
pub fn log_message_trans(key: impl Into<String>) { pub fn log_message_trans(key: impl Into<String>) {
APP_STATE.lock().unwrap().push_log(from_key(&key.into())); APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
tokio::spawn(async move { tokio::spawn(async move {
*UNIQUE.write().await = true; *UNIQUE.write().await = true;
}); });
} }
pub fn log_message(msg: impl Into<String>) { pub fn log_message(msg: impl Into<String>) {
APP_STATE.lock().unwrap().push_log(msg.into()); APP_STATE.lock().unwrap().push_log(msg.into());
tokio::spawn(async move { tokio::spawn(async move {
*UNIQUE.write().await = true; *UNIQUE.write().await = true;
}); });
} }
pub fn log_message_format(msg: impl Into<String>, args: &[&str]) { pub fn log_message_format(msg: impl Into<String>, args: &[&str]) {
APP_STATE APP_STATE
.lock() .lock()
.unwrap() .unwrap()
.push_log(format(&msg.into(), args)); .push_log(format(&msg.into(), args));
tokio::spawn(async move { tokio::spawn(async move {
*UNIQUE.write().await = true; *UNIQUE.write().await = true;
}); });
} }
pub fn setup() { pub fn setup() {
tokio::spawn(async move { tokio::spawn(async move {
{ {
ACTIVE_TASKS.lock().unwrap().push("metrics".to_string()); ACTIVE_TASKS.lock().unwrap().push("metrics".to_string());
} }
let mut sys = System::new_with_specifics(RefreshKind::new()); let mut sys = System::new_with_specifics(RefreshKind::new());
let mut last_total_received = 0u64; let mut last_total_received = 0u64;
let mut last_total_transmitted = 0u64; let mut last_total_transmitted = 0u64;
let mut counter = 0.0; let mut counter = 0.0;
loop { loop {
if *SHUTDOWN.read().await { if *SHUTDOWN.read().await {
break; break;
} }
sys.refresh_all(); sys.refresh_all();
let mut tcpu = 0; let mut tcpu = 0;
for cpu in sys.cpus() { for cpu in sys.cpus() {
tcpu += cpu.cpu_usage() as i64; tcpu += cpu.cpu_usage() as i64;
tcpu /= 2; tcpu /= 2;
} }
let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0; let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0;
let total_received = 0u64; let total_received = 0u64;
let total_transmitted = 0u64; let total_transmitted = 0u64;
let delta_received = if last_total_received == 0 { let delta_received = if last_total_received == 0 {
0 0
} else { } else {
total_received.saturating_sub(last_total_received) total_received.saturating_sub(last_total_received)
}; };
let delta_transmitted = if last_total_transmitted == 0 { let delta_transmitted = if last_total_transmitted == 0 {
0 0
} else { } else {
total_transmitted.saturating_sub(last_total_transmitted) total_transmitted.saturating_sub(last_total_transmitted)
}; };
last_total_received = total_received; last_total_received = total_received;
last_total_transmitted = total_transmitted; last_total_transmitted = total_transmitted;
let net_down = delta_received as f64; let net_down = delta_received as f64;
let net_up = delta_transmitted as f64; let net_up = delta_transmitted as f64;
{ {
let mut st = APP_STATE.lock().unwrap(); let mut st = APP_STATE.lock().unwrap();
st.push_cpu((counter, tcpu as f64)); st.push_cpu((counter, tcpu as f64));
st.push_ram((counter, ram)); st.push_ram((counter, ram));
st.push_net_down((counter, net_down)); st.push_net_down((counter, net_down));
st.push_net_up((counter, net_up)); st.push_net_up((counter, net_up));
st.sys_info = format!("NetDown: {} NetUp: {}", delta_received, delta_transmitted); st.sys_info = format!("NetDown: {} NetUp: {}", delta_received, delta_transmitted);
} }
counter += 1.0; counter += 1.0;
*UNIQUE.write().await = true; *UNIQUE.write().await = true;
thread::sleep(Duration::from_millis(1000)); thread::sleep(Duration::from_millis(1000));
} }
{ {
ACTIVE_TASKS ACTIVE_TASKS
.lock() .lock()
.unwrap() .unwrap()
.retain(|t| !t.eq(&"metrics".to_string())); .retain(|t| !t.eq(&"metrics".to_string()));
} }
}); });
} }

View file

@ -348,11 +348,7 @@ impl OmikronConnection {
} }
if cv.is_type(CommunicationType::identification_response) { if cv.is_type(CommunicationType::identification_response) {
if let Some(accepted) = cv.get_data(DataTypes::accepted) { if let Some(accepted) = cv.get_data(DataTypes::accepted) {
if accepted.as_str().unwrap_or("0") != "0" { log_message(format!("Omikron connected: {}", accepted.to_string()));
log_message(format!("Omikron connected: {}", accepted.to_string()));
} else {
log_message("omikron_connection_failed");
}
} }
return; return;
} }

View file

@ -1,4 +1,4 @@
pub mod contact; pub mod contact;
pub mod user_community_util; pub mod user_community_util;
pub mod user_manager; pub mod user_manager;
pub mod user_profile; pub mod user_profile;

View file

@ -1,129 +1,129 @@
use aes_gcm::{ use aes_gcm::{
Aes256Gcm, Nonce, Aes256Gcm, Nonce,
aead::{Aead, KeyInit, OsRng}, aead::{Aead, KeyInit, OsRng},
}; };
use base64::{Engine as _, engine::general_purpose::STANDARD}; use base64::{Engine as _, engine::general_purpose::STANDARD};
use rand_core::RngCore; use rand_core::RngCore;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use x448::{PublicKey, Secret, SharedSecret}; use x448::{PublicKey, Secret, SharedSecret};
/// Errors for crypto opertions /// Errors for crypto opertions
#[derive(Debug)] #[derive(Debug)]
pub enum CryptoError { pub enum CryptoError {
Base64Decode(base64::DecodeError), Base64Decode(base64::DecodeError),
InvalidKey, InvalidKey,
AgreementError, AgreementError,
EncryptionError(aes_gcm::Error), EncryptionError(aes_gcm::Error),
DecryptionError(aes_gcm::Error), DecryptionError(aes_gcm::Error),
} }
impl From<base64::DecodeError> for CryptoError { impl From<base64::DecodeError> for CryptoError {
fn from(err: base64::DecodeError) -> Self { fn from(err: base64::DecodeError) -> Self {
CryptoError::Base64Decode(err) CryptoError::Base64Decode(err)
} }
} }
pub struct KeyPair { pub struct KeyPair {
pub secret: Secret, pub secret: Secret,
pub public: PublicKey, pub public: PublicKey,
} }
pub fn generate_keypair() -> KeyPair { pub fn generate_keypair() -> KeyPair {
let mut buf = [0u8; 56]; let mut buf = [0u8; 56];
let mut rng = OsRng; let mut rng = OsRng;
rng.fill_bytes(&mut buf); rng.fill_bytes(&mut buf);
let secret = Secret::from_bytes(&buf).unwrap(); let secret = Secret::from_bytes(&buf).unwrap();
let public = PublicKey::from(&secret); let public = PublicKey::from(&secret);
KeyPair { secret, public } KeyPair { secret, public }
} }
pub fn public_key_to_base64(pubkey: &PublicKey) -> String { pub fn public_key_to_base64(pubkey: &PublicKey) -> String {
STANDARD.encode(pubkey.as_bytes().as_ref()) STANDARD.encode(pubkey.as_bytes().as_ref())
} }
pub fn secret_key_to_base64(secret: &Secret) -> String { pub fn secret_key_to_base64(secret: &Secret) -> String {
STANDARD.encode(secret.as_bytes().as_ref()) STANDARD.encode(secret.as_bytes().as_ref())
} }
pub fn load_public_key(base64_pub: &str) -> Option<PublicKey> { pub fn load_public_key(base64_pub: &str) -> Option<PublicKey> {
let bytes = STANDARD.decode(base64_pub).unwrap(); let bytes = STANDARD.decode(base64_pub).unwrap();
PublicKey::from_bytes(&bytes) PublicKey::from_bytes(&bytes)
} }
pub fn load_secret_key(base64_secret: &str) -> Option<Secret> { pub fn load_secret_key(base64_secret: &str) -> Option<Secret> {
let bytes = STANDARD.decode(base64_secret).unwrap(); let bytes = STANDARD.decode(base64_secret).unwrap();
Secret::from_bytes(&bytes) Secret::from_bytes(&bytes)
} }
fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] { fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(shared.as_bytes()); hasher.update(shared.as_bytes());
let result = hasher.finalize(); let result = hasher.finalize();
let mut key = [0u8; 32]; let mut key = [0u8; 32];
key.copy_from_slice(&result[..32]); key.copy_from_slice(&result[..32]);
key key
} }
pub fn encrypt( pub fn encrypt(
base64_secret: &str, base64_secret: &str,
base64_peer_pub: &str, base64_peer_pub: &str,
plaintext: &str, plaintext: &str,
) -> Result<String, CryptoError> { ) -> Result<String, CryptoError> {
let secret = load_secret_key(base64_secret).unwrap(); let secret = load_secret_key(base64_secret).unwrap();
let peer_pub = load_public_key(base64_peer_pub).unwrap(); let peer_pub = load_public_key(base64_peer_pub).unwrap();
let shared = secret let shared = secret
.to_diffie_hellman(&peer_pub) .to_diffie_hellman(&peer_pub)
.ok_or(CryptoError::AgreementError)?; .ok_or(CryptoError::AgreementError)?;
let key_bytes = derive_aes_key(&shared); let key_bytes = derive_aes_key(&shared);
let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct");
let mut nonce_bytes = [0u8; 12]; let mut nonce_bytes = [0u8; 12];
OsRng.fill_bytes(&mut nonce_bytes); OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes); let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher let ciphertext = cipher
.encrypt(nonce, plaintext.as_bytes()) .encrypt(nonce, plaintext.as_bytes())
.map_err(CryptoError::EncryptionError)?; .map_err(CryptoError::EncryptionError)?;
// prefix nonce to ciphertext // prefix nonce to ciphertext
let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len()); let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len());
out.extend_from_slice(&nonce_bytes); out.extend_from_slice(&nonce_bytes);
out.extend_from_slice(&ciphertext); out.extend_from_slice(&ciphertext);
Ok(STANDARD.encode(&out)) Ok(STANDARD.encode(&out))
} }
pub fn decrypt( pub fn decrypt(
base64_secret: &str, base64_secret: &str,
base64_peer_pub: &str, base64_peer_pub: &str,
encrypted_base64: &str, encrypted_base64: &str,
) -> Result<String, CryptoError> { ) -> Result<String, CryptoError> {
let secret = load_secret_key(base64_secret).unwrap(); let secret = load_secret_key(base64_secret).unwrap();
let peer_pub = load_public_key(base64_peer_pub).unwrap(); let peer_pub = load_public_key(base64_peer_pub).unwrap();
let shared = secret let shared = secret
.to_diffie_hellman(&peer_pub) .to_diffie_hellman(&peer_pub)
.ok_or(CryptoError::AgreementError)?; .ok_or(CryptoError::AgreementError)?;
let key_bytes = derive_aes_key(&shared); let key_bytes = derive_aes_key(&shared);
let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct");
let encrypted = STANDARD.decode(encrypted_base64)?; let encrypted = STANDARD.decode(encrypted_base64)?;
if encrypted.len() < 12 { if encrypted.len() < 12 {
return Err(CryptoError::DecryptionError(aes_gcm::Error)); return Err(CryptoError::DecryptionError(aes_gcm::Error));
} }
let nonce_bytes = &encrypted[..12]; let nonce_bytes = &encrypted[..12];
let ciphertext = &encrypted[12..]; let ciphertext = &encrypted[12..];
let nonce = Nonce::from_slice(nonce_bytes); let nonce = Nonce::from_slice(nonce_bytes);
let plaintext_bytes = cipher let plaintext_bytes = cipher
.decrypt(nonce, ciphertext) .decrypt(nonce, ciphertext)
.map_err(CryptoError::DecryptionError)?; .map_err(CryptoError::DecryptionError)?;
let plaintext = String::from_utf8(plaintext_bytes) let plaintext = String::from_utf8(plaintext_bytes)
.map_err(|_| CryptoError::DecryptionError(aes_gcm::Error))?; .map_err(|_| CryptoError::DecryptionError(aes_gcm::Error))?;
Ok(plaintext) Ok(plaintext)
} }
pub fn hash_it(input: &str) -> Vec<u8> { pub fn hash_it(input: &str) -> Vec<u8> {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(input.as_bytes()); hasher.update(input.as_bytes());
hasher.finalize().to_vec() hasher.finalize().to_vec()
} }
pub fn hex_hash(input: &str) -> String { pub fn hex_hash(input: &str) -> String {
let digest = hash_it(input); let digest = hash_it(input);
digest.iter().map(|b| format!("{:02x}", b)).collect() digest.iter().map(|b| format!("{:02x}", b)).collect()
} }

1004
src/util/crypto_util.rs Normal file

File diff suppressed because it is too large Load diff