use aes_gcm::aead::Aead; use aes_gcm::{Aes256Gcm, KeyInit, Nonce}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use rand::RngCore; const CIPHER_VERSION: u8 = 0x01; /// AES-256-GCM 加密。输出格式: Base64(0x01 || nonce[12] || ciphertext + tag) pub fn encrypt(key: &[u8; 32], plaintext: &str) -> Result { let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| e.to_string())?; let mut nonce_bytes = [0u8; 12]; rand::thread_rng().fill_bytes(&mut nonce_bytes); let nonce = Nonce::from_slice(&nonce_bytes); let ciphertext = cipher .encrypt(nonce, plaintext.as_bytes()) .map_err(|e| e.to_string())?; let mut combined = vec![CIPHER_VERSION]; combined.extend_from_slice(&nonce_bytes); combined.extend_from_slice(&ciphertext); Ok(BASE64.encode(&combined)) } /// AES-256-GCM 解密。支持 v1 格式: Base64(0x01 || nonce[12] || ciphertext + tag) /// 兼容旧格式: Base64(nonce[12] || ciphertext + tag) pub fn decrypt(key: &[u8; 32], encoded: &str) -> Result { let bytes = BASE64.decode(encoded).map_err(|e| e.to_string())?; if bytes.len() < 13 { return Err("ciphertext too short".into()); } let (nonce_bytes, ciphertext) = if bytes[0] == CIPHER_VERSION { // v1: version(1) + nonce(12) + ciphertext if bytes.len() < 14 { return Err("v1 ciphertext too short".into()); } (&bytes[1..13], &bytes[13..]) } else { // 旧格式: nonce(12) + ciphertext(向后兼容) (&bytes[0..12], &bytes[12..]) }; let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| e.to_string())?; let nonce = Nonce::from_slice(nonce_bytes); let plaintext = cipher .decrypt(nonce, ciphertext) .map_err(|e| e.to_string())?; String::from_utf8(plaintext).map_err(|e| e.to_string()) }