mockforge_plugin_loader/signature/
trust_store.rs1use super::{DEFAULT_TRUST_STORE_PATH, TRUST_STORE_ENV};
3use crate::{LoaderResult, PluginLoaderConfig, PluginLoaderError};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::{
7 fs,
8 path::{Path, PathBuf},
9};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TrustedKeyEntry {
14 pub key_id: String,
16 pub public_key: String,
18 #[serde(default)]
20 pub expires_at: Option<DateTime<Utc>>,
21 #[serde(default)]
23 pub revoked: bool,
24}
25
26#[derive(Debug, Clone, Default, Serialize, Deserialize)]
33pub struct TrustStoreFile {
34 #[serde(default)]
36 pub keys: Vec<TrustedKeyEntry>,
37}
38
39impl TrustStoreFile {
40 pub fn load(path: &Path) -> LoaderResult<Self> {
42 let raw = fs::read_to_string(path).map_err(|e| {
43 PluginLoaderError::security(format!(
44 "Failed to read trust store {}: {}",
45 path.display(),
46 e
47 ))
48 })?;
49 serde_json::from_str(&raw).map_err(|e| {
50 PluginLoaderError::security(format!(
51 "Failed to parse trust store {}: {}",
52 path.display(),
53 e
54 ))
55 })
56 }
57
58 pub fn apply_to(&self, config: &mut PluginLoaderConfig) -> LoaderResult<usize> {
61 let mut trusted = 0;
62 for entry in &self.keys {
63 if entry.revoked {
64 if !config.revoked_keys.contains(&entry.key_id) {
65 config.revoked_keys.push(entry.key_id.clone());
66 }
67 continue;
68 }
69 let key = hex::decode(entry.public_key.trim()).map_err(|e| {
70 PluginLoaderError::security(format!(
71 "Invalid public key hex for key '{}': {}",
72 entry.key_id, e
73 ))
74 })?;
75 if !config.trusted_keys.contains(&entry.key_id) {
76 config.trusted_keys.push(entry.key_id.clone());
77 }
78 config.key_data.insert(entry.key_id.clone(), key);
79 if let Some(expires_at) = entry.expires_at {
80 config.key_expires_at.insert(entry.key_id.clone(), expires_at);
81 }
82 trusted += 1;
83 }
84 Ok(trusted)
85 }
86}
87
88pub fn resolve_trust_store_path(explicit: Option<&Path>) -> Option<PathBuf> {
92 if let Some(path) = explicit {
93 return Some(path.to_path_buf());
94 }
95 if let Ok(path) = std::env::var(TRUST_STORE_ENV) {
96 if !path.trim().is_empty() {
97 return Some(PathBuf::from(shellexpand::tilde(path.trim()).as_ref()));
98 }
99 }
100 let default = PathBuf::from(shellexpand::tilde(DEFAULT_TRUST_STORE_PATH).as_ref());
101 default.exists().then_some(default)
102}