Создание шифрованного API

This commit is contained in:
2024-06-25 13:11:51 +07:00
parent 4345ddc645
commit 26d973a1a7
14 changed files with 776 additions and 66 deletions
+16 -2
View File
@@ -161,7 +161,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"block-padding",
"block-padding 0.2.1",
"generic-array",
]
@@ -180,7 +180,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
dependencies = [
"block-padding",
"block-padding 0.2.1",
"cipher",
]
@@ -190,6 +190,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "brotli"
version = "3.5.0"
@@ -3200,7 +3209,12 @@ dependencies = [
name = "to-do-app"
version = "0.0.0"
dependencies = [
"aes",
"block-modes",
"block-padding 0.3.3",
"hex",
"magic-crypt",
"rand 0.8.5",
"rust-fuzzy-search",
"serde",
"serde_json",
+7 -1
View File
@@ -16,6 +16,12 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
magic-crypt = "3.1.13"
rust-fuzzy-search = "0.1.1"
rand = "0.8.5"
hex = "0.4"
aes = "0.7.5"
block-modes = "0.8.1"
block-padding = "0.3.3"
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
@@ -23,6 +29,6 @@ custom-protocol = ["tauri/custom-protocol"]
[profile.release]
lto = true # Enables link to optimizations
opt-level = "z" # Optimize for binary size
opt-level = "s" # Optimize for binary size
strip = true # Remove debug symbols
+32
View File
@@ -0,0 +1,32 @@
use aes::Aes256;
use block_modes::{BlockMode, Cbc};
use block_modes::block_padding::Pkcs7;
use rand::Rng;
type Aes256Cbc = Cbc<Aes256, Pkcs7>;
pub fn encrypt(key: &[u8], data: &str) -> Vec<u8> {
let iv = rand::thread_rng().gen::<[u8; 16]>(); // Генерация соли
let cipher = Aes256Cbc::new_from_slices(key, &iv).unwrap(); // Создание шифратора
let ciphertext = cipher.encrypt_vec(data.as_bytes()); // Шифрование строки
let res: &[u8] = &ciphertext[..];
[&iv, res].concat() // Вывод данных в виде 256-битного массива
}
pub fn decrypt(key: &[u8], encrypted_data: &[u8]) -> String {
let iv = &encrypted_data[0..16]; // Получение соли из данных
let ciphertext = &encrypted_data[16..]; // Получение текста из данных
let cipher = Aes256Cbc::new_from_slices(key, iv).unwrap(); // Создание дешифратора
let decrypted_ciphertext = cipher.decrypt_vec(ciphertext).unwrap(); // Дешифрование данных
String::from_utf8(decrypted_ciphertext).unwrap() // Вывод результата в виде строки
}
/// Примеры
//println!("TEST ENC: {:?}", crate::aes_enc_dec::encrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), "{\"command\": \"status1\"}"));
//println!("TEST DEC: {:?}\n", crate::aes_enc_dec::decrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), &crate::aes_enc_dec::encrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), "{\"command\": \"status1\"}")));
//println!("REQ HEX: {:?}", req.trim());
//println!("REQ u8: {:?}\n", hex::decode(req.trim()).unwrap());
//println!("DEC AES: {:?}", crate::aes_enc_dec::decrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), &hex::decode(req.trim()).unwrap()))
+121
View File
@@ -0,0 +1,121 @@
use std::fs::{File, read_to_string};
use std::path::Path;
use serde_json::{json, Value};
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
use rand::Rng;
use rand::distributions::Uniform; // 0.8
#[tauri::command]
pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
path.push("ToDo");
path.push("config_api");
path.set_extension("enc");
let exist: bool = Path::new(&path).exists();
if exist{
let content = read_to_string(path);
return match content {
Ok(_) => {},
Err(_) => {
println!("Не удалось открыть конфигурационный файл!");
}
}
} else{
let path = Path::new(&path);
let prefix = path.parent().unwrap();
let file = std::fs::create_dir_all(prefix);
match file{
Ok(_) => {},
Err(err) => println!("Не удалось создать директории! \n{}", err)
}
let file = File::create(path);
match file{
Ok(_) => {
let rand_s: String = rand::thread_rng()
.sample_iter(Uniform::new(char::from(32), char::from(126)))
.take(64)
.map(char::from)
.collect();
let token: String = rand_s.replace("'", "").replace('"', "")
.replace("{", "").replace("}", "").replace("[", "")
.replace("}", "").replace("\\", "").replace("'", "");
let data = String::from(format!("{}\
\"port\": 49494,\
\"autostart\": true,\
\"token\": \"{}\"\
{}", "{", token, "}"));
println!("{}", data);
encrypt_n_save_file(path.into(), data);
},
Err(err) => println!("Не удалось создать файл! \n{}", err)
}
}
}
#[tauri::command]
pub fn get_port(app_handle: tauri::AppHandle) -> i64 {
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
path.push("ToDo");
path.push("config_api");
path.set_extension("enc");
let content = decrypt_file(path);
let data: Value = serde_json::from_str(content.as_str()).unwrap();
return data["port"].as_i64().unwrap();
}
#[tauri::command]
pub fn get_autostart(app_handle: tauri::AppHandle) -> bool {
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
path.push("ToDo");
path.push("config_api");
path.set_extension("enc");
let content = decrypt_file(path);
let data: Value = serde_json::from_str(content.as_str()).unwrap();
return data["autostart"].as_bool().unwrap();
}
#[tauri::command]
pub fn edit_settings(app_handle: tauri::AppHandle, port: i32, autostart: bool) {
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
path.push("ToDo");
path.push("config_api");
path.set_extension("enc");
let content = decrypt_file(path.clone());
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
data["port"] = json!(port);
data["autostart"] = json!(autostart);
encrypt_n_save_file(path.into(), data.to_string());
}
#[tauri::command]
pub fn reset_token(app_handle: tauri::AppHandle) -> String {
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
path.push("ToDo");
path.push("config_api");
path.set_extension("enc");
let key = rand::thread_rng().gen::<[u8; 32]>(); // 256-битный ключ
let content = decrypt_file(path.clone());
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
data["token"] = json!(hex::encode(&key.clone()));
encrypt_n_save_file(path.into(), data.to_string());
return hex::encode(&key.clone());
}
@@ -0,0 +1,146 @@
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::path::PathBuf;
use std::thread;
use std::string::String;
use serde_json::Value;
use tauri::{AppHandle, Manager};
use crate::config_tcp;
use crate::enc_dec_file::decrypt_file;
use crate::aes_enc_dec::{encrypt, decrypt};
static mut TCP_COMMAND: &str = "";
static mut PATH_CONFIG: Option<&'static str> = None;
fn get_token() -> String{
unsafe {
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
let data: Value = serde_json::from_str(content.as_str()).unwrap();
return data["token"].as_str().unwrap().to_string();
}
} // Получение токена доступа
fn data_to_enc_hex(data: &str) -> String{
hex::encode(encrypt(&hex::decode(get_token()).unwrap(), data))
} // Шифрование строки до данных обёрнутых в HEX
fn enc_hex_to_data(data: &str) -> String{
decrypt(&hex::decode(get_token()).unwrap(), &hex::decode(data).unwrap())
} // Расшифровка обёрнутых в HEX данных до строки
pub fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
let path: PathBuf = [
app_handle.path_resolver().app_local_data_dir().unwrap(),
"ToDo".into(),
"config_api.enc".into(),
].iter().collect(); // Путь к конфиг файлу
let path_str = Box::leak(Box::new(path.to_string_lossy().into_owned()));
unsafe {
PATH_CONFIG = Some(path_str);
}
return match TcpListener::bind(format!("127.0.0.1:{}", config_tcp::get_port(app_handle.clone()))){ // Создание TCP слушателя на локальном порту
Ok(tcp) => {
for stream in tcp.incoming() {
unsafe {
TCP_COMMAND = "";
}
let stream = stream.unwrap(); // Получение запроса
let thread = thread::spawn(move || { // Создание отдельного потока
handle_connection(stream); // Обработка запроса
});
thread.join().unwrap(); // Ожидание пока завершится поток
unsafe {
match TCP_COMMAND { // Сравнивание команд
"stop" => {
app_handle.emit_all("update_api", "").unwrap();
break;
}
_ => {}
}
}
};
tcp
},
Err(err) => {
println!("{}", err);
TcpListener::bind("").unwrap()
}
};
}
fn handle_connection(mut stream: TcpStream) {
let mut buf_reader = BufReader::new(&mut stream);
let mut req = String::new(); // Получение body запроса
match buf_reader.read_line(&mut req) {
Ok(_) => {
if req.len() > 0 {
let res = enc_hex_to_data(req.trim()); // Дешифрование
let json: Value = serde_json::from_str(res.as_str()).unwrap_or(serde_json::json!("")); // Попытка преобразования запроса в JSON
if json != serde_json::json!(""){
println!("JSON command: {:?}", json["command"]);
match json["command"].as_str().unwrap() { // Сравнивание команд
"stop" => {
unsafe {
TCP_COMMAND = "stop";
}
},
"status" => {
let res = encrypt(&hex::decode(get_token()).unwrap(), "200");
stream.write(hex::encode(res).as_bytes()).unwrap();
},
_ => {}
}
}
}
},
what => {
println!("{:?}", what);
}
}
}
#[tauri::command]
pub fn start_tcp_server(app_handle: AppHandle){
let handle = app_handle.clone();
thread::spawn(move || { // Создание потока
create_tcp_listener(handle); // Создание слушателя
});
app_handle.emit_all("update_api", "").unwrap(); // Глобальное сообщение для фронтенда для обновления API страницы
}
#[tauri::command]
pub fn get_status(app_handle: AppHandle) -> bool{
match TcpListener::bind(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) { // Получения статуса слушателя, с помощью нового слушателя
Ok(_) => {
false // Если удалось создать возвращаем false, т.к. слушателя не было
},
Err(_) => {
true // Иначе true
}
}
}
#[tauri::command]
pub fn send_command_to_server(app_handle: AppHandle, command: String) -> bool{
match TcpStream::connect(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) { // Подключение к слушателю
Ok(mut tcp) => {
tcp.write_all(data_to_enc_hex(format!("{}: \"{}\" {}", "{\"command\"", command, "}\n").as_str()).as_bytes()).unwrap(); // Отправление команды
true
},
Err(err) => {
println!("{}", err);
false
}
}
}
+49
View File
@@ -0,0 +1,49 @@
use std::fs::{read_to_string, write};
use std::path::PathBuf;
use magic_crypt::{MagicCryptTrait, new_magic_crypt};
use crate::config;
pub fn encrypt_n_save_file(path: PathBuf, content: String){
let key = config::ENC_KEY;
let mc = new_magic_crypt!(key, 256);
let mut encrypted: String = String::new();
for line in content.lines() {
encrypted = encrypted.to_owned() + mc.encrypt_str_to_base64(line).as_str() + "\n";
}
let res = write(path, encrypted);
match res {
Ok(_) => {},
Err(_) => println!("File save error!")
}
}
pub fn decrypt_file(path: PathBuf) -> String {
let key = config::ENC_KEY;
let mc = new_magic_crypt!(key, 256);
let mut decrypted: String = String::new();
let lines = read_to_string(path);
match lines {
Ok(lines) => {
for line in lines.lines() {
let dec_line = mc.decrypt_base64_to_string(&line);
match dec_line {
Ok(line) => {
decrypted = decrypted.to_owned() + line.as_str() + "\n";
return decrypted;
},
Err(_) => {
println!("Что-то пошло не так!");
}
}
}
return String::new();
},
Err(_) => {
println!("Что-то не так...");
return String::new();
}
}
}
+49 -2
View File
@@ -1,11 +1,49 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::thread;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[path="api/multithread_tcp_listener.rs"] mod multi_tcp_listener;
#[path= "api/config_tcp.rs"] mod config_tcp;
#[path= "api/aes_enc_dec.rs"] mod aes_enc_dec;
#[path="tasks_functions.rs"] mod tasks;
mod config;
#[path="config.rs"] mod config;
#[path="enc_dec_file.rs"] mod enc_dec_file;
#[tauri::command]
fn open_api_window(app_handle: tauri::AppHandle){
thread::spawn(move || {
match tauri::WindowBuilder::new(
&app_handle,
"api",
tauri::WindowUrl::App("api.html".into())
).title("Api")
//.skip_taskbar(true)
.decorations(false)
.build() {
Ok(_) => {}
Err(err) => {
println!("{err}");
}
}
});
}
fn main() {
tauri::Builder::default()
.setup(|app| {
let app_handle = app.handle();
if config_tcp::get_autostart(app_handle.clone()) {
thread::spawn(move || {
multi_tcp_listener::create_tcp_listener(app_handle);
});
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
tasks::check_or_create_tasks_file,
tasks::get_tasks,
@@ -13,7 +51,16 @@ fn main() {
tasks::add_task,
tasks::edit_task,
tasks::delete_task,
tasks::set_task_field
tasks::set_task_field,
open_api_window,
multi_tcp_listener::start_tcp_server,
multi_tcp_listener::get_status,
multi_tcp_listener::send_command_to_server,
config_tcp::check_or_create_config_file,
config_tcp::get_port,
config_tcp::get_autostart,
config_tcp::edit_settings,
config_tcp::reset_token
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
+2 -48
View File
@@ -1,56 +1,10 @@
use magic_crypt::{MagicCryptTrait, new_magic_crypt};
use std::fs::{read_to_string, write};
use std::path::PathBuf;
use std::fs::{read_to_string};
use std::path::Path;
use std::fs::File;
use serde_json::{Value, json};
use rust_fuzzy_search::fuzzy_search_best_n;
use crate::config;
pub fn encrypt_n_save_file(path: PathBuf, content: String){
let key = config::ENC_KEY;
let mc = new_magic_crypt!(key, 256);
let mut encrypted: String = String::new();
for line in content.lines() {
encrypted = encrypted.to_owned() + mc.encrypt_str_to_base64(line).as_str() + "\n";
}
let res = write(path, encrypted);
match res {
Ok(_) => {},
Err(_) => println!("File save error!")
}
}
pub fn decrypt_file(path: PathBuf) -> String {
let key = config::ENC_KEY;
let mc = new_magic_crypt!(key, 256);
let mut decrypted: String = String::new();
let lines = read_to_string(path);
match lines {
Ok(lines) => {
for line in lines.lines() {
let dec_line = mc.decrypt_base64_to_string(&line);
match dec_line {
Ok(line) => {
decrypted = decrypted.to_owned() + line.as_str() + "\n";
return decrypted;
},
Err(_) => {
println!("Что-то пошло не так!");
}
}
}
return String::new();
},
Err(_) => {
println!("Что-то не так...");
return String::new();
}
}
}
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
#[tauri::command]
pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {