Skip to main content

flare_core/common/platform/
encryption.rs

1//! AES-256-GCM encryption key resolution for Native / WASM.
2
3use std::sync::{Arc, Mutex, OnceLock};
4
5use crate::common::encryption::{Aes256GcmEncryptor, EncryptionUtil};
6use crate::common::error::{FlareError, Result};
7
8use super::env::optional_env;
9
10/// Required length for AES-256-GCM keys.
11pub const AES256_KEY_LEN: usize = 32;
12
13static RUNTIME_ENCRYPTION_KEY: OnceLock<Mutex<Option<Vec<u8>>>> = OnceLock::new();
14
15fn encryption_key_slot() -> &'static Mutex<Option<Vec<u8>>> {
16    RUNTIME_ENCRYPTION_KEY.get_or_init(|| Mutex::new(None))
17}
18
19/// Set encryption key at runtime (WASM/JS 注入;Native 测试亦可用).
20pub fn set_runtime_encryption_key(key: Vec<u8>) -> Result<()> {
21    if key.len() != AES256_KEY_LEN {
22        return Err(FlareError::protocol_error(format!(
23            "encryption key must be exactly {AES256_KEY_LEN} bytes, got {}",
24            key.len()
25        )));
26    }
27    *encryption_key_slot()
28        .lock()
29        .map_err(|_| FlareError::general_error("encryption key lock poisoned"))? = Some(key);
30    Ok(())
31}
32
33/// Clear a previously injected runtime key.
34pub fn clear_runtime_encryption_key() {
35    if let Ok(mut slot) = encryption_key_slot().lock() {
36        *slot = None;
37    }
38}
39
40/// Whether a runtime key was injected via [`set_runtime_encryption_key`].
41pub fn has_runtime_encryption_key() -> bool {
42    encryption_key_slot()
43        .lock()
44        .ok()
45        .and_then(|slot| slot.as_ref().map(|key| !key.is_empty()))
46        .unwrap_or(false)
47}
48
49/// Parse UTF-8 key string (must be exactly 32 bytes).
50pub fn parse_encryption_key_utf8(key: &str) -> Result<Vec<u8>> {
51    let bytes = key.as_bytes();
52    if bytes.len() != AES256_KEY_LEN {
53        return Err(FlareError::protocol_error(format!(
54            "encryption key string must be exactly {AES256_KEY_LEN} UTF-8 bytes, got {}",
55            bytes.len()
56        )));
57    }
58    Ok(bytes.to_vec())
59}
60
61/// Parse hex-encoded 32-byte key (64 hex chars).
62pub fn parse_encryption_key_hex(hex: &str) -> Result<Vec<u8>> {
63    let bytes = crate::common::utils::hex_to_bytes(hex).map_err(|error| {
64        FlareError::protocol_error(format!("invalid encryption key hex: {error}"))
65    })?;
66    if bytes.len() != AES256_KEY_LEN {
67        return Err(FlareError::protocol_error(format!(
68            "encryption key hex must decode to {AES256_KEY_LEN} bytes, got {}",
69            bytes.len()
70        )));
71    }
72    Ok(bytes)
73}
74
75/// Resolve key bytes: runtime injection > `ENCRYPTION_KEY` env (Native) > demo default.
76pub fn resolve_encryption_key_bytes(default_demo_key: Option<&[u8; AES256_KEY_LEN]>) -> Vec<u8> {
77    if let Ok(slot) = encryption_key_slot().lock()
78        && let Some(key) = slot.as_ref()
79    {
80        return key.clone();
81    }
82    if let Some(value) = optional_env("ENCRYPTION_KEY") {
83        return value.into_bytes();
84    }
85    default_demo_key.map(|key| key.to_vec()).unwrap_or_default()
86}
87
88/// Register AES-256-GCM encryptor using [`resolve_encryption_key_bytes`].
89pub fn register_aes256_encryption(default_demo_key: Option<&[u8; AES256_KEY_LEN]>) -> Result<()> {
90    let key = resolve_encryption_key_bytes(default_demo_key);
91    if key.len() != AES256_KEY_LEN {
92        return Err(FlareError::protocol_error(format!(
93            "encryption key must be exactly {AES256_KEY_LEN} bytes, got {} (set flare_set_encryption_key or ENCRYPTION_KEY)",
94            key.len()
95        )));
96    }
97    let encryptor = Aes256GcmEncryptor::new(&key)?;
98    EncryptionUtil::register_custom(Arc::new(encryptor));
99    Ok(())
100}