Skip to main content

encryption/
guard.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! 请求守卫:交给处理器的一个共享加密句柄。
4//!
5//! 框架集成层(axum / actix-web / rocket / poem / salvo / warp / bee-rust / e-cat)
6//! 全都在这一层之上做薄适配 —— 把 [`Guard`] 按各自框架的惯例注入请求上下文,
7//! 处理器拿到它就能加解密,不必把主密钥或 Manager 一路透传下去。
8//!
9//! ```no_run
10//! use encryption::guard::Guard;
11//!
12//! let master_key = [7u8; 32]; // 生产环境从环境变量 / KMS 读取
13//! let guard = Guard::from_master_key(&master_key, "aes-256-gcm")?;
14//!
15//! // 处理器里
16//! let stored = guard.encrypt("13800138000".as_bytes())?;
17//! assert_eq!(guard.decrypt(&stored)?, "13800138000".as_bytes());
18//! # Ok::<(), encryption::Error>(())
19//! ```
20//!
21//! # 为什么是 `Arc`
22//!
23//! 每个请求都要拿到同一个 Manager,而框架普遍要求注入的状态是 `Send + Sync + 'static`
24//! 且常常要求 `Clone`。Manager 本身是只读的、构造后不再变,所以 `Arc` 共享即可 ——
25//! 子密钥只在构造时派生一次,每请求零成本。
26
27use std::sync::Arc;
28
29use crate::error::Result;
30use crate::manager::EncryptionManager;
31
32/// 请求守卫:一个可跨请求共享、可跨线程克隆的加密句柄。
33///
34/// 构造之后的一切加解密都经由它转发到 [`EncryptionManager`];克隆只是
35/// `Arc` 引用计数递增,不含任何密钥拷贝。
36#[derive(Debug, Clone)]
37pub struct Guard {
38    manager: Arc<EncryptionManager>,
39}
40
41impl Guard {
42    /// 从一个已经建好的 Manager 构造。
43    pub fn new(manager: EncryptionManager) -> Self {
44        Self {
45            manager: Arc::new(manager),
46        }
47    }
48
49    /// 从 32 字节主密钥构造(内部走 [`crate::factory::EncryptionManagerFactory`])。
50    ///
51    /// 构造时即完成全部校验(主密钥长度、默认算法是否注册),配置错了就在启动时炸。
52    pub fn from_master_key(master_key: &[u8], default_identifier: &str) -> Result<Self> {
53        Ok(Self::new(
54            crate::factory::EncryptionManagerFactory::from_master_key(
55                master_key,
56                default_identifier,
57            )?,
58        ))
59    }
60
61    /// 共享的 Manager。总是存在。
62    pub fn manager(&self) -> &EncryptionManager {
63        &self.manager
64    }
65
66    /// 用默认算法加密。
67    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
68        self.manager.encrypt(plaintext)
69    }
70
71    /// 用默认算法解密。
72    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
73        self.manager.decrypt(ciphertext)
74    }
75
76    /// 用指定算法加密。
77    pub fn encrypt_with(&self, identifier: &str, plaintext: &[u8]) -> Result<Vec<u8>> {
78        self.manager.encrypt_with(identifier, plaintext)
79    }
80
81    /// 用指定算法解密。
82    pub fn decrypt_with(&self, identifier: &str, ciphertext: &[u8]) -> Result<Vec<u8>> {
83        self.manager.decrypt_with(identifier, ciphertext)
84    }
85
86    /// 已注册算法标识(健康检查 / 日志用)。
87    pub fn identifiers(&self) -> Vec<&str> {
88        self.manager.registry().identifiers()
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    fn guard() -> Guard {
97        Guard::from_master_key(&[7u8; 32], "aes-256-gcm").unwrap()
98    }
99
100    #[test]
101    fn wraps_the_factory_and_forwards() {
102        let guard = guard();
103        assert_eq!(guard.identifiers().len(), 5);
104        assert_eq!(guard.manager().default_identifier(), "aes-256-gcm");
105
106        let blob = guard.encrypt(b"phone").unwrap();
107        assert_eq!(guard.decrypt(&blob).unwrap(), b"phone");
108
109        let blob = guard.encrypt_with("sm4-cbc", b"phone").unwrap();
110        assert_eq!(guard.decrypt_with("sm4-cbc", &blob).unwrap(), b"phone");
111    }
112
113    #[test]
114    fn clones_share_one_manager() {
115        let guard = guard();
116        let clone = guard.clone();
117        let blob = guard.encrypt(b"shared").unwrap();
118        assert_eq!(clone.decrypt(&blob).unwrap(), b"shared");
119        assert_eq!(Arc::strong_count(&guard.manager), 2);
120    }
121
122    #[test]
123    fn invalid_master_key_fails_at_construction() {
124        assert!(Guard::from_master_key(&[0u8; 16], "aes-256-gcm").is_err());
125        assert!(Guard::from_master_key(&[0u8; 32], "no-such-algorithm").is_err());
126    }
127}