coreason_runtime_rust/execution_plane/
integrity.rs1use dashmap::DashMap;
19use ed25519_dalek::{Signature, Verifier, VerifyingKey};
20use sha2::{Digest, Sha256};
21use std::convert::TryInto;
22use std::sync::OnceLock;
23
24fn verified_cache() -> &'static DashMap<String, String> {
29 static CACHE: OnceLock<DashMap<String, String>> = OnceLock::new();
30 CACHE.get_or_init(DashMap::new)
31}
32
33#[derive(Debug, Clone)]
35pub struct IntegrityViolationError {
36 pub message: String,
37}
38
39impl std::fmt::Display for IntegrityViolationError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "IntegrityViolation: {}", self.message)
42 }
43}
44
45impl std::error::Error for IntegrityViolationError {}
46
47pub fn check_cache(module_name: &str, expected_hash: &str) -> bool {
49 verified_cache()
50 .get(module_name)
51 .map_or(false, |v| v.value() == expected_hash)
52}
53
54pub fn update_cache(module_name: &str, expected_hash: &str) {
56 verified_cache().insert(module_name.to_string(), expected_hash.to_string());
57}
58
59pub fn clear_cache() {
61 verified_cache().clear();
62}
63
64pub fn verify_module_integrity(source_code: &str, expected_sha256: &str) -> bool {
69 let normalized = source_code.replace("\r\n", "\n");
70 let mut hasher = Sha256::new();
71 hasher.update(normalized.as_bytes());
72 let actual_hash = format!("{:x}", hasher.finalize());
73 actual_hash == expected_sha256
74}
75
76pub fn verify_file_hash(file_path: &str, expected_sha256: &str) -> Result<bool, String> {
80 use memmap2::Mmap;
81 use std::fs::File;
82
83 let file = File::open(file_path).map_err(|e| format!("Failed to open file: {}", e))?;
84 let mmap = unsafe { Mmap::map(&file).map_err(|e| format!("Failed to mmap file: {}", e))? };
85
86 let mut normalized = Vec::with_capacity(mmap.len());
88 let mut i = 0;
89 while i < mmap.len() {
90 if i + 1 < mmap.len() && mmap[i] == b'\r' && mmap[i + 1] == b'\n' {
91 normalized.push(b'\n');
92 i += 2;
93 } else {
94 normalized.push(mmap[i]);
95 i += 1;
96 }
97 }
98
99 let mut hasher = Sha256::new();
100 hasher.update(&normalized);
101 let actual_hash = format!("{:x}", hasher.finalize());
102 Ok(actual_hash == expected_sha256)
103}
104
105pub fn verify_signature(
109 message: &str,
110 signature_hex: &str,
111 public_key_hex: &str,
112) -> Result<bool, String> {
113 let public_bytes = hex::decode(public_key_hex).map_err(|e| e.to_string())?;
114 let signature_bytes = hex::decode(signature_hex).map_err(|e| e.to_string())?;
115
116 let public_array: [u8; 32] = public_bytes
117 .as_slice()
118 .try_into()
119 .map_err(|_| "Public key must be 32 bytes".to_string())?;
120 let signature_array: [u8; 64] = signature_bytes
121 .as_slice()
122 .try_into()
123 .map_err(|_| "Signature must be 64 bytes".to_string())?;
124
125 let verifying_key = VerifyingKey::from_bytes(&public_array)
126 .map_err(|e| format!("Invalid public key: {}", e))?;
127 let signature = Signature::from_bytes(&signature_array);
128
129 Ok(verifying_key.verify(message.as_bytes(), &signature).is_ok())
130}
131
132pub fn assert_module_integrity(
137 module_name: &str,
138 source_code: &str,
139 expected_hash: &str,
140) -> Result<(), IntegrityViolationError> {
141 if check_cache(module_name, expected_hash) {
143 return Ok(());
144 }
145
146 if !verify_module_integrity(source_code, expected_hash) {
148 let normalized = source_code.replace("\r\n", "\n");
149 let mut hasher = Sha256::new();
150 hasher.update(normalized.as_bytes());
151 let actual_hash = format!("{:x}", hasher.finalize());
152
153 return Err(IntegrityViolationError {
154 message: format!(
155 "CRITICAL INTEGRITY VIOLATION: Module {} has been tampered with! \
156 Expected hash {}... but got {}...",
157 module_name,
158 &expected_hash[..expected_hash.len().min(8)],
159 &actual_hash[..actual_hash.len().min(8)],
160 ),
161 });
162 }
163
164 update_cache(module_name, expected_hash);
166 Ok(())
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_verify_module_integrity() {
175 let source = "fn main() { println!(\"hello\"); }";
176 let mut hasher = Sha256::new();
177 hasher.update(source.as_bytes());
178 let expected = format!("{:x}", hasher.finalize());
179
180 assert!(verify_module_integrity(source, &expected));
181 assert!(!verify_module_integrity(source, "badhash"));
182 }
183
184 #[test]
185 fn test_verify_module_normalizes_crlf() {
186 let source_lf = "line1\nline2";
187 let source_crlf = "line1\r\nline2";
188
189 let mut hasher = Sha256::new();
190 hasher.update(source_lf.as_bytes());
191 let expected = format!("{:x}", hasher.finalize());
192
193 assert!(verify_module_integrity(source_lf, &expected));
194 assert!(verify_module_integrity(source_crlf, &expected));
195 }
196
197 #[test]
198 fn test_cache_operations() {
199 let test_key = "test_mod_ops_unique";
200 update_cache(test_key, "hash123");
202 assert!(check_cache(test_key, "hash123"));
203 assert!(!check_cache(test_key, "wrong_hash"));
204 }
205
206 #[test]
207 fn test_assert_module_integrity_valid() {
208 let test_key = "test_mod_valid_unique";
209 let source = "test source code";
210 let mut hasher = Sha256::new();
211 hasher.update(source.as_bytes());
212 let expected = format!("{:x}", hasher.finalize());
213
214 assert!(assert_module_integrity(test_key, source, &expected).is_ok());
215 assert!(check_cache(test_key, &expected));
217 }
218
219 #[test]
220 fn test_assert_module_integrity_tampered() {
221 let test_key = "test_mod_tampered_unique";
222 let source = "original source";
223 let result = assert_module_integrity(test_key, source, "badhash0000");
224 assert!(result.is_err());
225 assert!(result
226 .unwrap_err()
227 .message
228 .contains("CRITICAL INTEGRITY VIOLATION"));
229 }
230
231 #[test]
232 fn test_assert_module_integrity_cached() {
233 let unique_key = format!(
235 "cached_mod_{}_{}",
236 std::process::id(),
237 std::time::SystemTime::now()
238 .duration_since(std::time::UNIX_EPOCH)
239 .unwrap()
240 .as_nanos()
241 );
242 update_cache(&unique_key, "known_hash");
244 assert!(check_cache(&unique_key, "known_hash"));
245 assert!(assert_module_integrity(&unique_key, "doesn't matter", "known_hash").is_ok());
247 }
248
249 #[test]
250 fn test_hex_decode() {
251 assert_eq!(hex::decode("48656c6c6f").unwrap(), b"Hello");
252 assert!(hex::decode("abc").is_err()); assert!(hex::decode("zz").is_err()); }
255}