nucleus/security/
policy.rs1use crate::error::{NucleusError, Result};
8use serde::de::DeserializeOwned;
9use sha2::{Digest, Sha256};
10use std::path::Path;
11use tracing::info;
12
13pub fn sha256_hex(data: &[u8]) -> String {
15 let mut hasher = Sha256::new();
16 hasher.update(data);
17 hex::encode(hasher.finalize())
18}
19
20pub fn read_and_verify(path: &Path, expected_sha256: Option<&str>) -> Result<Vec<u8>> {
25 let content = std::fs::read(path).map_err(|e| {
26 NucleusError::ConfigError(format!("Failed to read policy file {:?}: {}", path, e))
27 })?;
28
29 if let Some(expected) = expected_sha256 {
30 let actual = sha256_hex(&content);
31 if actual != expected {
32 return Err(NucleusError::ConfigError(format!(
33 "Policy file {:?} hash mismatch: expected {}, got {}",
34 path, expected, actual
35 )));
36 }
37 info!("Policy file {:?} hash verified: {}", path, actual);
38 }
39
40 Ok(content)
41}
42
43pub fn load_toml_policy<T: DeserializeOwned>(
45 path: &Path,
46 expected_sha256: Option<&str>,
47) -> Result<T> {
48 let content = read_and_verify(path, expected_sha256)?;
49 let text = std::str::from_utf8(&content).map_err(|e| {
50 NucleusError::ConfigError(format!("Policy file {:?} is not valid UTF-8: {}", path, e))
51 })?;
52 toml::from_str(text).map_err(|e| {
53 NucleusError::ConfigError(format!("Failed to parse TOML policy {:?}: {}", path, e))
54 })
55}
56
57pub fn load_json_policy<T: DeserializeOwned>(
59 path: &Path,
60 expected_sha256: Option<&str>,
61) -> Result<T> {
62 let content = read_and_verify(path, expected_sha256)?;
63 serde_json::from_slice(&content).map_err(|e| {
64 NucleusError::ConfigError(format!("Failed to parse JSON policy {:?}: {}", path, e))
65 })
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_sha256_hex() {
74 let hash = sha256_hex(b"hello world");
75 assert_eq!(
76 hash,
77 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
78 );
79 }
80}