use crate::crypto::error::CryptoError;
use crate::utils::permissions::{create_directory_with_700_perms, set_file_permissions};
use aes_gcm::aead::rand_core::RngCore;
use aes_gcm::aead::OsRng;
use argon2::{Algorithm, Argon2, Params, Version};
use base64::{engine::general_purpose, Engine as _};
use std::path::PathBuf;
const KEY_LEN: usize = 32;
fn keys_dir() -> Result<PathBuf, CryptoError> {
let home = dirs::home_dir()
.ok_or_else(|| CryptoError::KeyNotFound("无法确定用户主目录".to_string()))?;
Ok(home.join(".agentswitch").join("keys"))
}
fn master_key_path() -> Result<PathBuf, CryptoError> {
Ok(keys_dir()?.join("master.key"))
}
pub fn generate_key() -> [u8; KEY_LEN] {
let mut key = [0u8; KEY_LEN];
OsRng.fill_bytes(&mut key);
key
}
fn save_key_to_file(key: &[u8; KEY_LEN], path: &PathBuf) -> Result<(), CryptoError> {
if let Some(parent) = path.parent() {
create_directory_with_700_perms(parent)
.map_err(|e| CryptoError::Io(std::io::Error::other(e.to_string())))?;
}
std::fs::write(path, key)?;
set_file_permissions(path)
.map_err(|e| CryptoError::Io(std::io::Error::other(e.to_string())))?;
Ok(())
}
fn load_key_from_file(path: &PathBuf) -> Result<[u8; KEY_LEN], CryptoError> {
if !path.exists() {
return Err(CryptoError::KeyNotFound(format!(
"密钥文件不存在: {}",
path.display()
)));
}
let data = std::fs::read(path)?;
if data.len() != KEY_LEN {
return Err(CryptoError::KeyInvalid(format!(
"密钥文件长度无效: 期望 {} 字节,实际 {} 字节",
KEY_LEN,
data.len()
)));
}
let mut key = [0u8; KEY_LEN];
key.copy_from_slice(&data);
Ok(key)
}
pub fn generate_and_save_master_key() -> Result<[u8; KEY_LEN], CryptoError> {
let path = master_key_path()?;
if path.exists() {
return Err(CryptoError::KeyAlreadyExists(format!(
"主密钥已存在: {}。如需重新生成,请先删除旧密钥文件。",
path.display()
)));
}
let key = generate_key();
save_key_to_file(&key, &path)?;
Ok(key)
}
pub fn load_master_key() -> Result<[u8; KEY_LEN], CryptoError> {
let path = master_key_path()?;
load_key_from_file(&path)
}
pub fn master_key_exists() -> Result<bool, CryptoError> {
let path = master_key_path()?;
Ok(path.exists())
}
pub fn export_key_to_base64(key: &[u8; KEY_LEN]) -> String {
general_purpose::STANDARD.encode(key)
}
pub fn import_key_from_base64(b64: &str) -> Result<[u8; KEY_LEN], CryptoError> {
let decoded = general_purpose::STANDARD
.decode(b64.trim())
.map_err(|e| CryptoError::KeyInvalid(format!("Base64 解码失败: {}", e)))?;
if decoded.len() != KEY_LEN {
return Err(CryptoError::KeyInvalid(format!(
"密钥长度无效: 期望 {} 字节,实际 {} 字节",
KEY_LEN,
decoded.len()
)));
}
let mut key = [0u8; KEY_LEN];
key.copy_from_slice(&decoded);
Ok(key)
}
pub fn import_and_save_master_key(b64: &str) -> Result<[u8; KEY_LEN], CryptoError> {
let path = master_key_path()?;
if path.exists() {
return Err(CryptoError::KeyAlreadyExists(format!(
"主密钥已存在: {}。如需覆盖,请先删除旧密钥文件。",
path.display()
)));
}
let key = import_key_from_base64(b64)?;
save_key_to_file(&key, &path)?;
Ok(key)
}
pub fn get_master_key_path_str() -> Result<String, CryptoError> {
let path = master_key_path()?;
Ok(path.display().to_string())
}
pub struct KeyDerivation {
argon: Argon2<'static>,
}
impl KeyDerivation {
pub fn new() -> Self {
Self {
argon: Argon2::new(Algorithm::Argon2id, Version::V0x13, Params::default()),
}
}
pub fn with_params(m_cost: u32, t_cost: u32, p_cost: u32) -> Result<Self, CryptoError> {
let params = Params::new(m_cost, t_cost, p_cost, None)
.map_err(|e| CryptoError::KeyDerivationFailed(format!("无效的参数: {}", e)))?;
Ok(Self {
argon: Argon2::new(Algorithm::Argon2id, Version::V0x13, params),
})
}
pub fn derive_key(&self, password: &str, salt: &[u8; 32]) -> Result<[u8; 32], CryptoError> {
let mut key = [0u8; 32];
self.argon
.hash_password_into(password.as_bytes(), salt, &mut key)
.map_err(|e| CryptoError::KeyDerivationFailed(format!("密钥派生失败: {}", e)))?;
Ok(key)
}
pub fn generate_salt() -> Result<[u8; 32], CryptoError> {
let mut salt = [0u8; 32];
OsRng.fill_bytes(&mut salt);
Ok(salt)
}
}
impl Default for KeyDerivation {
fn default() -> Self {
Self::new()
}
}