Skip to main content

nucleus/security/
policy.rs

1//! Shared policy file loading infrastructure.
2//!
3//! Provides generic TOML and JSON policy loaders with optional SHA-256
4//! integrity verification. Used by caps_policy, landlock_policy, and
5//! seccomp profile loading.
6
7use crate::error::{NucleusError, Result};
8use serde::de::DeserializeOwned;
9use sha2::{Digest, Sha256};
10use std::path::Path;
11use tracing::info;
12
13/// Compute the SHA-256 hex digest of a byte slice.
14pub fn sha256_hex(data: &[u8]) -> String {
15    let mut hasher = Sha256::new();
16    hasher.update(data);
17    hex::encode(hasher.finalize())
18}
19
20/// Read a file and optionally verify its SHA-256 hash.
21///
22/// Returns the raw file contents on success. If `expected_sha256` is provided
23/// and the hash doesn't match, returns an error.
24pub 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
43/// Load and parse a TOML policy file with optional SHA-256 verification.
44pub 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
57/// Load and parse a JSON policy file with optional SHA-256 verification.
58pub 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}