aster/codesign/
storage.rs1use std::collections::HashMap;
4use std::path::PathBuf;
5use std::sync::RwLock;
6
7use once_cell::sync::Lazy;
8
9use super::types::{CodeSignature, SigningKey};
10
11fn get_signing_dir() -> PathBuf {
13 dirs::home_dir()
14 .unwrap_or_else(|| PathBuf::from("."))
15 .join(".aster")
16 .join("signing")
17}
18
19fn get_keys_file() -> PathBuf {
21 get_signing_dir().join("keys.json")
22}
23
24fn get_signatures_file() -> PathBuf {
26 get_signing_dir().join("signatures.json")
27}
28
29static SIGNATURE_CACHE: Lazy<RwLock<HashMap<String, CodeSignature>>> =
31 Lazy::new(|| RwLock::new(HashMap::new()));
32
33pub fn init_signing() {
35 let dir = get_signing_dir();
36 if !dir.exists() {
37 let _ = std::fs::create_dir_all(&dir);
38 #[cfg(unix)]
39 {
40 use std::os::unix::fs::PermissionsExt;
41 let _ = std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o700));
42 }
43 }
44}
45
46pub fn save_key(key: &SigningKey) -> Result<(), String> {
48 init_signing();
49
50 let mut keys = load_keys();
51 keys.retain(|k| k.id != key.id);
52 keys.push(key.clone());
53
54 let json = serde_json::to_string_pretty(&keys)
55 .map_err(|e| format!("Failed to serialize keys: {}", e))?;
56
57 std::fs::write(get_keys_file(), json).map_err(|e| format!("Failed to write keys: {}", e))?;
58
59 Ok(())
60}
61
62pub fn load_keys() -> Vec<SigningKey> {
64 let file = get_keys_file();
65 if !file.exists() {
66 return Vec::new();
67 }
68
69 std::fs::read_to_string(&file)
70 .ok()
71 .and_then(|s| serde_json::from_str(&s).ok())
72 .unwrap_or_default()
73}
74
75pub fn cache_signature(path: &str, signature: CodeSignature) {
77 if let Ok(mut cache) = SIGNATURE_CACHE.write() {
78 cache.insert(path.to_string(), signature);
79 }
80}
81
82pub fn get_cached_signature(path: &str) -> Option<CodeSignature> {
84 if let Ok(cache) = SIGNATURE_CACHE.read() {
86 if let Some(sig) = cache.get(path) {
87 return Some(sig.clone());
88 }
89 }
90
91 load_signatures();
93
94 SIGNATURE_CACHE.read().ok()?.get(path).cloned()
95}
96
97pub fn save_signatures() {
99 init_signing();
100
101 let signatures: HashMap<String, CodeSignature> = SIGNATURE_CACHE
102 .read()
103 .map(|c| c.clone())
104 .unwrap_or_default();
105
106 if let Ok(json) = serde_json::to_string_pretty(&signatures) {
107 let _ = std::fs::write(get_signatures_file(), json);
108 }
109}
110
111pub fn load_signatures() {
113 let file = get_signatures_file();
114 if !file.exists() {
115 return;
116 }
117
118 if let Ok(content) = std::fs::read_to_string(&file) {
119 if let Ok(sigs) = serde_json::from_str::<HashMap<String, CodeSignature>>(&content) {
120 if let Ok(mut cache) = SIGNATURE_CACHE.write() {
121 for (path, sig) in sigs {
122 cache.insert(path, sig);
123 }
124 }
125 }
126 }
127}
128
129pub fn clear_signature(file_path: &str) {
131 use std::path::Path;
132
133 let absolute_path = Path::new(file_path)
134 .canonicalize()
135 .map(|p| p.to_string_lossy().to_string())
136 .unwrap_or_else(|_| file_path.to_string());
137
138 if let Ok(mut cache) = SIGNATURE_CACHE.write() {
139 cache.remove(&absolute_path);
140 }
141 save_signatures();
142}
143
144pub fn get_signed_files() -> Vec<(String, CodeSignature)> {
146 load_signatures();
147
148 SIGNATURE_CACHE
149 .read()
150 .map(|c| c.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
151 .unwrap_or_default()
152}
153
154pub fn is_signed(file_path: &str) -> bool {
156 use std::path::Path;
157
158 let absolute_path = Path::new(file_path)
159 .canonicalize()
160 .map(|p| p.to_string_lossy().to_string())
161 .unwrap_or_else(|_| file_path.to_string());
162
163 load_signatures();
164
165 SIGNATURE_CACHE
166 .read()
167 .map(|c| c.contains_key(&absolute_path))
168 .unwrap_or(false)
169}