1use std::collections::HashMap;
4use std::sync::RwLock;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9pub enum CipherOp {
10 Encrypt,
11 Decrypt,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum CipherAlgorithm {
16 Aes256Gcm,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct EncryptedField {
21 pub table: String,
22 pub field: String,
23 pub algorithm: CipherAlgorithm,
24 pub key_id: String,
25}
26
27#[derive(Debug, Clone, Default, Serialize, Deserialize)]
28pub struct FieldCipherConfig {
29 pub encrypted_fields: Vec<EncryptedField>,
30}
31
32impl FieldCipherConfig {
33 pub fn find(&self, table: &str, field: &str) -> Option<&EncryptedField> {
34 self.encrypted_fields
35 .iter()
36 .find(|f| f.table == table && f.field == field)
37 }
38}
39
40pub struct FieldCipher {
41 config: FieldCipherConfig,
42 keys: RwLock<HashMap<String, Vec<u8>>>,
43}
44
45impl FieldCipher {
46 pub fn new(config: FieldCipherConfig) -> Self {
47 Self {
48 config,
49 keys: RwLock::new(HashMap::new()),
50 }
51 }
52
53 pub fn add_key(&self, key_id: &str, key: Vec<u8>) {
54 self.keys.write().unwrap().insert(key_id.to_string(), key);
55 }
56
57 pub fn process(
58 &self,
59 table: &str,
60 field: &str,
61 value: &str,
62 op: CipherOp,
63 ) -> Result<String, String> {
64 let field_config = self
65 .config
66 .find(table, field)
67 .ok_or_else(|| format!("字段 {}.{} 未配置加密", table, field))?;
68
69 let keys = self.keys.read().unwrap();
70 let key = keys
71 .get(&field_config.key_id)
72 .ok_or_else(|| format!("密钥 {} 不存在", field_config.key_id))?;
73
74 match op {
75 CipherOp::Encrypt => {
76 let encrypted = Self::xor_encrypt(value.as_bytes(), key);
77 Ok(hex_encode(&encrypted))
78 }
79 CipherOp::Decrypt => {
80 let ciphertext = hex_decode(value).map_err(|e| format!("hex 解码失败: {}", e))?;
81 let decrypted = Self::xor_encrypt(&ciphertext, key);
82 Ok(String::from_utf8(decrypted).map_err(|e| format!("UTF-8 解码失败: {}", e))?)
83 }
84 }
85 }
86
87 fn xor_encrypt(data: &[u8], key: &[u8]) -> Vec<u8> {
88 if key.is_empty() {
89 return data.to_vec();
90 }
91 data.iter()
92 .enumerate()
93 .map(|(i, b)| b ^ key[i % key.len()])
94 .collect()
95 }
96
97 pub fn config(&self) -> &FieldCipherConfig {
98 &self.config
99 }
100}
101
102fn hex_encode(data: &[u8]) -> String {
103 data.iter().map(|b| format!("{:02x}", b)).collect()
104}
105
106fn hex_decode(s: &str) -> Result<Vec<u8>, String> {
107 if !s.len().is_multiple_of(2) {
108 return Err("奇数长度".to_string());
109 }
110 (0..s.len())
111 .step_by(2)
112 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string()))
113 .collect()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn encrypt_decrypt_roundtrip() {
122 let config = FieldCipherConfig {
123 encrypted_fields: vec![EncryptedField {
124 table: "users".to_string(),
125 field: "phone".to_string(),
126 algorithm: CipherAlgorithm::Aes256Gcm,
127 key_id: "k1".to_string(),
128 }],
129 };
130 let cipher = FieldCipher::new(config);
131 cipher.add_key("k1", vec![0x42; 32]);
132
133 let original = "13800001234";
134 let encrypted = cipher
135 .process("users", "phone", original, CipherOp::Encrypt)
136 .unwrap();
137 assert_ne!(encrypted, original, "密文不应等于明文");
138 let decrypted = cipher
139 .process("users", "phone", &encrypted, CipherOp::Decrypt)
140 .unwrap();
141 assert_eq!(decrypted, original);
142 }
143
144 #[test]
145 fn different_fields_different_keys() {
146 let config = FieldCipherConfig {
147 encrypted_fields: vec![
148 EncryptedField {
149 table: "users".to_string(),
150 field: "phone".to_string(),
151 algorithm: CipherAlgorithm::Aes256Gcm,
152 key_id: "k1".to_string(),
153 },
154 EncryptedField {
155 table: "users".to_string(),
156 field: "email".to_string(),
157 algorithm: CipherAlgorithm::Aes256Gcm,
158 key_id: "k2".to_string(),
159 },
160 ],
161 };
162 let cipher = FieldCipher::new(config);
163 cipher.add_key("k1", vec![0x11; 32]);
164 cipher.add_key("k2", vec![0x22; 32]);
165
166 let phone_enc = cipher
167 .process("users", "phone", "13800001234", CipherOp::Encrypt)
168 .unwrap();
169 let email_enc = cipher
170 .process("users", "email", "test@test.com", CipherOp::Encrypt)
171 .unwrap();
172 assert_ne!(phone_enc, email_enc);
173 }
174
175 #[test]
176 fn unconfigured_field_errors() {
177 let cipher = FieldCipher::new(FieldCipherConfig::default());
178 let result = cipher.process("users", "name", "Alice", CipherOp::Encrypt);
179 assert!(result.is_err());
180 }
181
182 #[test]
183 fn missing_key_errors() {
184 let config = FieldCipherConfig {
185 encrypted_fields: vec![EncryptedField {
186 table: "t".to_string(),
187 field: "f".to_string(),
188 algorithm: CipherAlgorithm::Aes256Gcm,
189 key_id: "missing".to_string(),
190 }],
191 };
192 let cipher = FieldCipher::new(config);
193 let result = cipher.process("t", "f", "data", CipherOp::Encrypt);
194 assert!(result.is_err());
195 }
196
197 #[test]
198 fn hex_encode_decode_roundtrip() {
199 let data = vec![0x01, 0x02, 0xff, 0xab];
200 let encoded = hex_encode(&data);
201 let decoded = hex_decode(&encoded).unwrap();
202 assert_eq!(decoded, data);
203 }
204
205 #[test]
206 fn wiring_public_api() {
207 let config = FieldCipherConfig::default();
208 let cipher = FieldCipher::new(config);
209 assert!(cipher.config().encrypted_fields.is_empty());
210 }
211}
212#[cfg(feature = "tde-interceptor")]
217mod tde {
218 use std::collections::HashMap;
219 use std::sync::Arc;
220
221 use sz_orm_crypto::{ColumnEncryptionPolicy, CryptoError, KmsClient, KmsError};
222
223 use crate::value::Value;
224
225 #[derive(Debug)]
227 pub enum TdeError {
228 KmsUnavailable(String),
230 KeyVersionNotFound(String),
232 AlgoNotSupported(String),
234 PolicyNotFound(String),
236 CryptoFailed(String),
238 }
239
240 impl std::fmt::Display for TdeError {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 match self {
243 TdeError::KmsUnavailable(msg) => write!(f, "KMS unavailable: {}", msg),
244 TdeError::KeyVersionNotFound(msg) => write!(f, "Key version not found: {}", msg),
245 TdeError::AlgoNotSupported(msg) => write!(f, "Algorithm not supported: {}", msg),
246 TdeError::PolicyNotFound(msg) => write!(f, "Policy not found: {}", msg),
247 TdeError::CryptoFailed(msg) => write!(f, "Crypto failed: {}", msg),
248 }
249 }
250 }
251
252 impl std::error::Error for TdeError {}
253
254 impl From<KmsError> for TdeError {
255 fn from(e: KmsError) -> Self {
256 match e {
257 KmsError::KmsUnavailable(msg) => TdeError::KmsUnavailable(msg),
258 KmsError::KeyVersionNotFound(msg) => TdeError::KeyVersionNotFound(msg),
259 KmsError::AlgoNotSupported(msg) => TdeError::AlgoNotSupported(msg),
260 KmsError::TlsConfigInvalid(msg) => TdeError::KmsUnavailable(msg),
261 KmsError::DegradeTimeout(msg) => TdeError::KmsUnavailable(msg),
262 }
263 }
264 }
265
266 impl From<CryptoError> for TdeError {
267 fn from(e: CryptoError) -> Self {
268 TdeError::CryptoFailed(e.to_string())
269 }
270 }
271
272 pub struct TdeInterceptor {
276 policy: ColumnEncryptionPolicy,
277 kms: Arc<dyn KmsClient>,
278 }
279
280 impl TdeInterceptor {
281 pub fn new(policy: ColumnEncryptionPolicy, kms: Arc<dyn KmsClient>) -> Self {
283 Self { policy, kms }
284 }
285
286 pub fn policy(&self) -> &ColumnEncryptionPolicy {
288 &self.policy
289 }
290
291 pub async fn encrypt_field(
296 &self,
297 table: &str,
298 column: &str,
299 value: &Value,
300 ) -> Result<Value, TdeError> {
301 let config = match self.policy.find(table, column) {
302 Some(c) => c,
303 None => return Ok(value.clone()),
304 };
305 let plaintext = value_to_bytes(value);
306 let dek = self
307 .kms
308 .get_dek(column, config.key_version)
309 .await
310 .map_err(TdeError::from)?;
311 let ciphertext = dek
312 .encrypt(&plaintext, config.algorithm)
313 .map_err(TdeError::from)?;
314 Ok(Value::Bytes(ciphertext))
315 }
316
317 pub async fn decrypt_field(
322 &self,
323 table: &str,
324 column: &str,
325 value: &Value,
326 ) -> Result<Value, TdeError> {
327 let config = match self.policy.find(table, column) {
328 Some(c) => c,
329 None => return Ok(value.clone()),
330 };
331 let ciphertext = match value {
332 Value::Bytes(b) => b.clone(),
333 Value::String(s) => hex_decode(s).unwrap_or_else(|_| s.as_bytes().to_vec()),
334 _ => return Ok(value.clone()),
335 };
336 let dek = self
337 .kms
338 .get_dek(column, config.key_version)
339 .await
340 .map_err(TdeError::from)?;
341 let plaintext = dek
342 .decrypt(&ciphertext, config.algorithm)
343 .map_err(TdeError::from)?;
344 bytes_to_value(&plaintext)
345 }
346
347 pub async fn encrypt_row(
349 &self,
350 table: &str,
351 row: &HashMap<String, Value>,
352 ) -> Result<HashMap<String, Value>, TdeError> {
353 let mut result = HashMap::new();
354 for (column, value) in row {
355 let encrypted = self.encrypt_field(table, column, value).await?;
356 result.insert(column.clone(), encrypted);
357 }
358 Ok(result)
359 }
360
361 pub async fn decrypt_row(
363 &self,
364 table: &str,
365 row: &HashMap<String, Value>,
366 ) -> Result<HashMap<String, Value>, TdeError> {
367 let mut result = HashMap::new();
368 for (column, value) in row {
369 let decrypted = self.decrypt_field(table, column, value).await?;
370 result.insert(column.clone(), decrypted);
371 }
372 Ok(result)
373 }
374 }
375
376 fn value_to_bytes(value: &Value) -> Vec<u8> {
377 match value {
378 Value::String(s) => s.as_bytes().to_vec(),
379 Value::Bytes(b) => b.clone(),
380 Value::I64(n) => n.to_le_bytes().to_vec(),
381 Value::Bool(b) => vec![*b as u8],
382 _ => value.to_param().as_bytes().to_vec(),
383 }
384 }
385
386 fn bytes_to_value(bytes: &[u8]) -> Result<Value, TdeError> {
387 String::from_utf8(bytes.to_vec())
388 .map(Value::String)
389 .map_err(|e| TdeError::CryptoFailed(format!("UTF-8 解码失败: {}", e)))
390 }
391
392 fn hex_decode(s: &str) -> Result<Vec<u8>, String> {
393 if !s.len().is_multiple_of(2) {
394 return Err("奇数长度".to_string());
395 }
396 (0..s.len())
397 .step_by(2)
398 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string()))
399 .collect()
400 }
401
402 #[cfg(test)]
403 mod tests {
404 use super::*;
405 use sz_orm_crypto::{ColumnCryptoConfig, LocalKmsClient};
406
407 fn setup() -> (TdeInterceptor, Vec<u8>) {
408 let dek_bytes = vec![0x42u8; 32];
409 let kms = Arc::new(LocalKmsClient::with_dek("ssn", 1, dek_bytes.clone()));
410 let policy =
411 ColumnEncryptionPolicy::from_configs(vec![
412 ColumnCryptoConfig::new("users", "ssn").with_key_version(1)
413 ]);
414 (TdeInterceptor::new(policy, kms), dek_bytes)
415 }
416
417 #[tokio::test]
418 async fn encrypt_decrypt_roundtrip() {
419 let (interceptor, _) = setup();
420 let plaintext = Value::String("123-45-6789".to_string());
421 let encrypted = interceptor
422 .encrypt_field("users", "ssn", &plaintext)
423 .await
424 .unwrap();
425 assert_ne!(encrypted, plaintext);
426 let decrypted = interceptor
427 .decrypt_field("users", "ssn", &encrypted)
428 .await
429 .unwrap();
430 assert_eq!(decrypted, plaintext);
431 }
432
433 #[tokio::test]
434 async fn unencrypted_column_passthrough() {
435 let (interceptor, _) = setup();
436 let value = Value::String("hello".to_string());
437 let result = interceptor
438 .encrypt_field("users", "name", &value)
439 .await
440 .unwrap();
441 assert_eq!(result, value);
442 }
443
444 #[tokio::test]
445 async fn decrypt_unencrypted_column_passthrough() {
446 let (interceptor, _) = setup();
447 let value = Value::String("hello".to_string());
448 let result = interceptor
449 .decrypt_field("users", "name", &value)
450 .await
451 .unwrap();
452 assert_eq!(result, value);
453 }
454
455 #[tokio::test]
456 async fn encrypt_row_batch() {
457 let (interceptor, _) = setup();
458 let mut row = HashMap::new();
459 row.insert("ssn".to_string(), Value::String("123-45-6789".to_string()));
460 row.insert("name".to_string(), Value::String("Alice".to_string()));
461 let encrypted = interceptor.encrypt_row("users", &row).await.unwrap();
462 assert_ne!(encrypted["ssn"], row["ssn"]);
463 assert_eq!(encrypted["name"], row["name"]);
464 let decrypted = interceptor.decrypt_row("users", &encrypted).await.unwrap();
465 assert_eq!(decrypted["ssn"], row["ssn"]);
466 }
467
468 #[tokio::test]
469 async fn key_not_found_error() {
470 let kms = Arc::new(LocalKmsClient::new());
471 let policy =
472 ColumnEncryptionPolicy::from_configs(vec![
473 ColumnCryptoConfig::new("users", "ssn").with_key_version(99)
474 ]);
475 let interceptor = TdeInterceptor::new(policy, kms);
476 let result = interceptor
477 .encrypt_field("users", "ssn", &Value::String("test".to_string()))
478 .await;
479 assert!(result.is_err());
480 }
481 }
482}
483
484#[cfg(feature = "tde-interceptor")]
485pub use tde::{TdeError, TdeInterceptor};