File size: 5,086 Bytes
a21c316 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
use base64::{engine::general_purpose, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
use sha2::Digest;
const FIXED_NONCE: &[u8; 12] = b"antigravsalt";
const ENCRYPTED_PREFIX: &str = "ag_enc_";
/// 生成加密密钥 (基于设备 ID)
fn get_encryption_key() -> [u8; 32] {
// 使用设备唯一标识生成密钥
let device_id = machine_uid::get().unwrap_or_else(|_| "default".to_string());
let mut key = [0u8; 32];
let hash = sha2::Sha256::digest(device_id.as_bytes());
key.copy_from_slice(&hash);
key
}
pub fn serialize_password<S>(password: &str, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// [FIX #1738] 防止双重加密:检查是否已包含魔术前缀
if password.starts_with(ENCRYPTED_PREFIX) {
return serializer.serialize_str(password);
}
let encrypted = encrypt_string(password).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encrypted)
}
pub fn deserialize_password<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let raw = String::deserialize(deserializer)?;
if raw.is_empty() {
return Ok(raw);
}
// [FIX #1738] 检查魔术前缀
if raw.starts_with(ENCRYPTED_PREFIX) {
// 新版格式:去前缀后解密
let ciphertext = &raw[ENCRYPTED_PREFIX.len()..];
match decrypt_string_internal(ciphertext) {
Ok(plaintext) => Ok(plaintext),
Err(_) => {
// 解密失败(如密钥变更),返回原始密文以防止数据丢失
Ok(raw)
}
}
} else {
// 兼容旧版:尝试直接解密
match decrypt_string_internal(&raw) {
Ok(plaintext) => {
// 只有当解密出有效的 UTF-8 且看起来像合理个字符串时才认为是旧版密文
// 这里 decrypt_string_internal 已经保证了 UTF-8,
// 如果是用户输入的明文,通常解密会失败(Base64 错误或 Tag 校验错误)。
Ok(plaintext)
}
Err(_) => {
// 解密失败,认为是普通明文(用户输入的无前缀密码)
Ok(raw)
}
}
}
}
pub fn encrypt_string(password: &str) -> Result<String, String> {
let key = get_encryption_key();
let cipher = Aes256Gcm::new(&key.into());
// In production, we should use a random nonce and prepend it to the ciphertext
// For simplicity in this demo, we use a fixed nonce (NOT SECURE for repeats)
// improving security: use random nonce
let nonce = Nonce::from_slice(FIXED_NONCE);
let ciphertext = cipher
.encrypt(nonce, password.as_bytes())
.map_err(|e| format!("Encryption failed: {}", e))?;
let base64_ciphertext = general_purpose::STANDARD.encode(ciphertext);
// [FIX #1738] 添加魔术前缀
Ok(format!("{}{}", ENCRYPTED_PREFIX, base64_ciphertext))
}
/// 内部解密函数 (输入必须是纯 Base64 密文,不含前缀)
fn decrypt_string_internal(encrypted_base64: &str) -> Result<String, String> {
let key = get_encryption_key();
let cipher = Aes256Gcm::new(&key.into());
let nonce = Nonce::from_slice(FIXED_NONCE);
let ciphertext = general_purpose::STANDARD
.decode(encrypted_base64)
.map_err(|e| format!("Base64 decode failed: {}", e))?;
let plaintext = cipher
.decrypt(nonce, ciphertext.as_ref())
.map_err(|e| format!("Decryption failed: {}", e))?;
String::from_utf8(plaintext).map_err(|e| format!("UTF-8 conversion failed: {}", e))
}
pub fn decrypt_string(encrypted: &str) -> Result<String, String> {
if encrypted.starts_with(ENCRYPTED_PREFIX) {
decrypt_string_internal(&encrypted[ENCRYPTED_PREFIX.len()..])
} else {
decrypt_string_internal(encrypted)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_cycle() {
let password = "my_secret_password";
let encrypted = encrypt_string(password).unwrap();
assert!(encrypted.starts_with(ENCRYPTED_PREFIX));
assert_ne!(password, encrypted);
let decrypted = decrypt_string(&encrypted).unwrap();
assert_eq!(password, decrypted);
}
#[test]
fn test_legacy_compatibility() {
// 模拟旧版加密(手动调用内部逻辑生成无前缀密文)
let password = "legacy_password";
let key = get_encryption_key();
let cipher = Aes256Gcm::new(&key.into());
let nonce = Nonce::from_slice(FIXED_NONCE);
let ciphertext = cipher.encrypt(nonce, password.as_bytes()).unwrap();
let legacy_encrypted = general_purpose::STANDARD.encode(ciphertext);
assert!(!legacy_encrypted.starts_with(ENCRYPTED_PREFIX));
// 使用新版解密逻辑
let decrypted = decrypt_string(&legacy_encrypted).unwrap();
assert_eq!(password, decrypted);
}
}
|