mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 04:43:44 +08:00
feat: encryption configuration properties
This commit is contained in:
parent
bb44fc51bd
commit
15bd7324fe
471
src-tauri/Cargo.lock
generated
471
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -55,6 +55,10 @@ tauri-plugin-devtools = "2.0.0-rc"
|
|||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
zip = "2.2.0"
|
zip = "2.2.0"
|
||||||
reqwest_dav = "0.1.14"
|
reqwest_dav = "0.1.14"
|
||||||
|
aes-gcm = { version = "0.10.3", features = ["std"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
getrandom = "0.2"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
runas = "=1.2.0"
|
runas = "=1.2.0"
|
||||||
deelevate = "0.2.0"
|
deelevate = "0.2.0"
|
||||||
|
98
src-tauri/src/config/encrypt.rs
Normal file
98
src-tauri/src/config/encrypt.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use crate::utils::dirs::get_encryption_key;
|
||||||
|
use aes_gcm::{
|
||||||
|
aead::{Aead, KeyInit},
|
||||||
|
Aes256Gcm, Key,
|
||||||
|
};
|
||||||
|
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
const NONCE_LENGTH: usize = 12;
|
||||||
|
|
||||||
|
/// Encrypt data
|
||||||
|
pub fn encrypt_data(data: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let encryption_key = get_encryption_key()?;
|
||||||
|
let key = Key::<Aes256Gcm>::from_slice(&encryption_key);
|
||||||
|
let cipher = Aes256Gcm::new(key);
|
||||||
|
|
||||||
|
// Generate random nonce
|
||||||
|
let mut nonce = vec![0u8; NONCE_LENGTH];
|
||||||
|
getrandom::getrandom(&mut nonce)?;
|
||||||
|
|
||||||
|
// Encrypt data
|
||||||
|
let ciphertext = cipher
|
||||||
|
.encrypt(nonce.as_slice().into(), data.as_bytes())
|
||||||
|
.map_err(|e| format!("Encryption failed: {}", e))?;
|
||||||
|
|
||||||
|
// Concatenate nonce and ciphertext and encode them in base64
|
||||||
|
let mut combined = nonce;
|
||||||
|
combined.extend(ciphertext);
|
||||||
|
Ok(STANDARD.encode(combined))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt data
|
||||||
|
pub fn decrypt_data(encrypted: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let encryption_key = get_encryption_key()?;
|
||||||
|
let key = Key::<Aes256Gcm>::from_slice(&encryption_key);
|
||||||
|
let cipher = Aes256Gcm::new(key);
|
||||||
|
// Decode from base64
|
||||||
|
let data = STANDARD.decode(encrypted)?;
|
||||||
|
if data.len() < NONCE_LENGTH {
|
||||||
|
return Err("Invalid encrypted data".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate nonce and ciphertext
|
||||||
|
let (nonce, ciphertext) = data.split_at(NONCE_LENGTH);
|
||||||
|
|
||||||
|
// Decrypt data
|
||||||
|
let plaintext = cipher
|
||||||
|
.decrypt(nonce.into(), ciphertext)
|
||||||
|
.map_err(|e| format!("Decryption failed: {}", e))?;
|
||||||
|
|
||||||
|
String::from_utf8(plaintext).map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize encrypted function
|
||||||
|
pub fn serialize_encrypted<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
// 如果序列化失败,返回 None
|
||||||
|
let json = match serde_json::to_string(value) {
|
||||||
|
Ok(j) => j,
|
||||||
|
Err(_) => return serializer.serialize_none(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果加密失败,返回 None
|
||||||
|
match encrypt_data(&json) {
|
||||||
|
Ok(encrypted) => serializer.serialize_str(&encrypted),
|
||||||
|
Err(_) => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize decrypted function
|
||||||
|
pub fn deserialize_encrypted<'a, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
T: for<'de> Deserialize<'de> + Default,
|
||||||
|
D: Deserializer<'a>,
|
||||||
|
{
|
||||||
|
// 如果反序列化字符串失败,返回默认值
|
||||||
|
println!("deserialize_encrypted");
|
||||||
|
let encrypted = match String::deserialize(deserializer) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return Ok(T::default()),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("encrypted: {}", encrypted);
|
||||||
|
// 如果解密失败,返回默认值
|
||||||
|
let decrypted_string = match decrypt_data(&encrypted) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return Ok(T::default()),
|
||||||
|
};
|
||||||
|
println!("decrypted_string: {}", decrypted_string);
|
||||||
|
// 如果 JSON 解析失败,返回默认值
|
||||||
|
match serde_json::from_str(&decrypted_string) {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(_) => Ok(T::default()),
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ mod clash;
|
|||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
mod config;
|
mod config;
|
||||||
mod draft;
|
mod draft;
|
||||||
|
mod encrypt;
|
||||||
mod prfitem;
|
mod prfitem;
|
||||||
mod profiles;
|
mod profiles;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
@ -10,6 +11,7 @@ mod verge;
|
|||||||
pub use self::clash::*;
|
pub use self::clash::*;
|
||||||
pub use self::config::*;
|
pub use self::config::*;
|
||||||
pub use self::draft::*;
|
pub use self::draft::*;
|
||||||
|
pub use self::encrypt::*;
|
||||||
pub use self::prfitem::*;
|
pub use self::prfitem::*;
|
||||||
pub use self::profiles::*;
|
pub use self::profiles::*;
|
||||||
pub use self::runtime::*;
|
pub use self::runtime::*;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::config::DEFAULT_PAC;
|
use crate::config::DEFAULT_PAC;
|
||||||
|
use crate::config::{deserialize_encrypted, serialize_encrypted};
|
||||||
use crate::utils::{dirs, help};
|
use crate::utils::{dirs, help};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
@ -148,8 +149,28 @@ pub struct IVerge {
|
|||||||
|
|
||||||
pub verge_http_enabled: Option<bool>,
|
pub verge_http_enabled: Option<bool>,
|
||||||
|
|
||||||
|
/// WebDAV 配置 (加密存储)
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "serialize_encrypted",
|
||||||
|
deserialize_with = "deserialize_encrypted",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
pub webdav_url: Option<String>,
|
pub webdav_url: Option<String>,
|
||||||
|
|
||||||
|
/// WebDAV 用户名 (加密存储)
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "serialize_encrypted",
|
||||||
|
deserialize_with = "deserialize_encrypted",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
pub webdav_username: Option<String>,
|
pub webdav_username: Option<String>,
|
||||||
|
|
||||||
|
/// WebDAV 密码 (加密存储)
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "serialize_encrypted",
|
||||||
|
deserialize_with = "deserialize_encrypted",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
pub webdav_password: Option<String>,
|
pub webdav_password: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::core::handle;
|
use crate::core::handle;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
@ -125,3 +126,27 @@ pub fn path_to_str(path: &PathBuf) -> Result<&str> {
|
|||||||
.ok_or(anyhow::anyhow!("failed to get path from {:?}", path))?;
|
.ok_or(anyhow::anyhow!("failed to get path from {:?}", path))?;
|
||||||
Ok(path_str)
|
Ok(path_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_encryption_key() -> Result<Vec<u8>> {
|
||||||
|
let app_dir = app_home_dir()?;
|
||||||
|
let key_path = app_dir.join(".encryption_key");
|
||||||
|
|
||||||
|
if key_path.exists() {
|
||||||
|
// Read existing key
|
||||||
|
fs::read(&key_path).map_err(|e| anyhow::anyhow!("Failed to read encryption key: {}", e))
|
||||||
|
} else {
|
||||||
|
// Generate and save new key
|
||||||
|
let mut key = vec![0u8; 32];
|
||||||
|
getrandom::getrandom(&mut key)?;
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
if let Some(parent) = key_path.parent() {
|
||||||
|
fs::create_dir_all(parent)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to create key directory: {}", e))?;
|
||||||
|
}
|
||||||
|
// Save key
|
||||||
|
fs::write(&key_path, &key)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to save encryption key: {}", e))?;
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user