1use crate::crypto::{CryptoError, KeyStore, Result};
59use parking_lot::RwLock;
60use serde::{Deserialize, Serialize};
61use sha2::{Digest, Sha256};
62use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
63use std::sync::Arc;
64use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
65use zeroize::Zeroize;
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub enum AnomalyType {
74 TimingAnomaly,
76 MemoryPressure,
78 SyscallAnomaly,
80 CryptoFailure,
82 SignatureAnomaly,
84 NonceReuse,
86 ExcessiveFailures,
88 ExternalTrigger,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
94pub enum Severity {
95 Info = 0,
97 Warning = 1,
99 Error = 2,
101 Critical = 3,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct AnomalyEvent {
108 pub anomaly_type: AnomalyType,
110 pub severity: Severity,
112 pub description: String,
114 pub timestamp: u64,
116 pub metadata: Option<String>,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
122pub enum PanicState {
123 Normal,
125 Elevated,
127 Frozen,
129 Destroyed,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct PanicLogEntry {
136 pub id: u64,
138 pub timestamp: u64,
140 pub from_state: PanicState,
142 pub to_state: PanicState,
143 pub trigger: AnomalyEvent,
145 pub prev_hash: [u8; 32],
147 pub entry_hash: [u8; 32],
148}
149
150#[allow(dead_code)]
156#[derive(Zeroize)]
157#[zeroize(drop)]
158struct DestructibleKey {
159 key_bytes: Vec<u8>,
161 canary: [u8; 32],
163}
164
165#[allow(dead_code)]
166impl DestructibleKey {
167 fn new(key_bytes: Vec<u8>) -> Self {
168 let mut canary = [0u8; 32];
169 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut canary);
170
171 DestructibleKey { key_bytes, canary }
172 }
173
174 fn verify_canary(&self) -> bool {
175 !self.canary.iter().all(|&b| b == 0)
177 }
178
179 fn destroy(&mut self) {
180 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut self.key_bytes);
182 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut self.canary);
183
184 self.key_bytes.zeroize();
186 self.canary.zeroize();
187 }
188}
189
190#[allow(clippy::type_complexity)]
192pub struct PanicProtectedKeyStore<K: KeyStore> {
193 inner: Arc<RwLock<Option<K>>>,
195
196 state: Arc<RwLock<PanicState>>,
198
199 anomaly_count: AtomicU64,
201
202 panic_threshold: u64,
204
205 frozen: AtomicBool,
207
208 destroyed: AtomicBool,
210
211 panic_log: Arc<RwLock<Vec<PanicLogEntry>>>,
213
214 alert_callback: Option<Box<dyn Fn(&PanicLogEntry) + Send + Sync>>,
216
217 last_op_time: Arc<RwLock<Instant>>,
219}
220
221impl<K: KeyStore> PanicProtectedKeyStore<K> {
222 pub fn new(inner: K, panic_threshold: u64) -> Self {
229 PanicProtectedKeyStore {
230 inner: Arc::new(RwLock::new(Some(inner))),
231 state: Arc::new(RwLock::new(PanicState::Normal)),
232 anomaly_count: AtomicU64::new(0),
233 panic_threshold,
234 frozen: AtomicBool::new(false),
235 destroyed: AtomicBool::new(false),
236 panic_log: Arc::new(RwLock::new(Vec::new())),
237 alert_callback: None,
238 last_op_time: Arc::new(RwLock::new(Instant::now())),
239 }
240 }
241
242 pub fn set_alert_callback<F>(&mut self, callback: F)
244 where
245 F: Fn(&PanicLogEntry) + Send + Sync + 'static,
246 {
247 self.alert_callback = Some(Box::new(callback));
248 }
249
250 pub fn report_anomaly(&self, anomaly: AnomalyEvent) {
252 let old_count = self.anomaly_count.fetch_add(1, Ordering::SeqCst);
253
254 match anomaly.severity {
255 Severity::Critical => {
256 self.trigger_panic(anomaly);
258 }
259 Severity::Error => {
260 if old_count + 1 >= self.panic_threshold {
261 self.trigger_panic(anomaly);
262 } else {
263 self.log_anomaly(anomaly);
264 }
265 }
266 _ => {
267 self.log_anomaly(anomaly);
268 }
269 }
270 }
271
272 fn trigger_panic(&self, trigger: AnomalyEvent) {
274 let old_state = *self.state.read();
275
276 if old_state == PanicState::Destroyed {
278 return;
279 }
280
281 self.frozen.store(true, Ordering::SeqCst);
283 *self.state.write() = PanicState::Frozen;
284
285 let log_entry = self.create_log_entry(old_state, PanicState::Destroyed, trigger);
287
288 if let Some(ref callback) = self.alert_callback {
290 callback(&log_entry);
291 }
292
293 self.panic_log.write().push(log_entry);
295
296 if let Some(ref mut _keystore) = *self.inner.write() {
298 }
300 *self.inner.write() = None;
301
302 self.destroyed.store(true, Ordering::SeqCst);
304 *self.state.write() = PanicState::Destroyed;
305 }
306
307 fn create_log_entry(
309 &self,
310 from_state: PanicState,
311 to_state: PanicState,
312 trigger: AnomalyEvent,
313 ) -> PanicLogEntry {
314 let log = self.panic_log.read();
315 let prev_hash = log.last().map(|e| e.entry_hash).unwrap_or([0u8; 32]);
316
317 let id = log.len() as u64;
318 let timestamp = SystemTime::now()
319 .duration_since(UNIX_EPOCH)
320 .unwrap()
321 .as_secs();
322
323 let mut hasher = Sha256::new();
325 hasher.update(id.to_le_bytes());
326 hasher.update(timestamp.to_le_bytes());
327 hasher.update([from_state as u8, to_state as u8]);
328 hasher.update(prev_hash);
329 let entry_hash = hasher.finalize().into();
330
331 PanicLogEntry {
332 id,
333 timestamp,
334 from_state,
335 to_state,
336 trigger,
337 prev_hash,
338 entry_hash,
339 }
340 }
341
342 fn log_anomaly(&self, _anomaly: AnomalyEvent) {
344 }
346
347 pub fn check_timing(&self, operation_name: &str) -> Result<TimingGuard> {
349 if self.is_destroyed() {
350 return Err(CryptoError::InvalidState("KeyStore destroyed".into()));
351 }
352
353 Ok(TimingGuard::new(
354 Arc::clone(&self.last_op_time),
355 operation_name.to_string(),
356 ))
357 }
358
359 pub fn state(&self) -> PanicState {
361 *self.state.read()
362 }
363
364 pub fn is_frozen(&self) -> bool {
366 self.frozen.load(Ordering::SeqCst)
367 }
368
369 pub fn is_destroyed(&self) -> bool {
371 self.destroyed.load(Ordering::SeqCst)
372 }
373
374 pub fn anomaly_count(&self) -> u64 {
376 self.anomaly_count.load(Ordering::SeqCst)
377 }
378
379 pub fn get_panic_log(&self) -> Vec<PanicLogEntry> {
381 self.panic_log.read().clone()
382 }
383
384 pub fn emergency_panic(&self, reason: &str) {
386 self.trigger_panic(AnomalyEvent {
387 anomaly_type: AnomalyType::ExternalTrigger,
388 severity: Severity::Critical,
389 description: reason.to_string(),
390 timestamp: SystemTime::now()
391 .duration_since(UNIX_EPOCH)
392 .unwrap()
393 .as_secs(),
394 metadata: None,
395 });
396 }
397}
398
399impl<K: KeyStore> KeyStore for PanicProtectedKeyStore<K> {
401 fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
402 if self.is_destroyed() {
403 return Err(CryptoError::InvalidState(
404 "KeyStore destroyed - keys zeroed".into(),
405 ));
406 }
407 if self.is_frozen() {
408 return Err(CryptoError::InvalidState(
409 "KeyStore frozen - panic triggered".into(),
410 ));
411 }
412
413 let guard = self.inner.read();
414 match guard.as_ref() {
415 Some(ks) => ks.sign(data),
416 None => Err(CryptoError::InvalidState("KeyStore unavailable".into())),
417 }
418 }
419
420 fn verify(&self, data: &[u8], signature: &[u8]) -> Result<()> {
421 if self.is_destroyed() {
423 return Err(CryptoError::InvalidState("KeyStore destroyed".into()));
424 }
425
426 let guard = self.inner.read();
427 match guard.as_ref() {
428 Some(ks) => ks.verify(data, signature),
429 None => Err(CryptoError::InvalidState("KeyStore unavailable".into())),
430 }
431 }
432
433 fn public_key_bytes(&self) -> Vec<u8> {
434 let guard = self.inner.read();
435 match guard.as_ref() {
436 Some(ks) => ks.public_key_bytes(),
437 None => vec![],
438 }
439 }
440
441 fn identifier(&self) -> String {
442 format!("PanicProtected(state={:?})", self.state())
443 }
444}
445
446pub struct TimingGuard {
452 start: Instant,
453 last_op: Arc<RwLock<Instant>>,
454 operation: String,
455 completed: bool,
456}
457
458impl TimingGuard {
459 fn new(last_op: Arc<RwLock<Instant>>, operation: String) -> Self {
460 TimingGuard {
461 start: Instant::now(),
462 last_op,
463 operation,
464 completed: false,
465 }
466 }
467
468 pub fn complete(mut self) -> Option<AnomalyEvent> {
470 self.completed = true;
471 let elapsed = self.start.elapsed();
472
473 if elapsed > Duration::from_secs(1) {
476 return Some(AnomalyEvent {
477 anomaly_type: AnomalyType::TimingAnomaly,
478 severity: Severity::Warning,
479 description: format!(
480 "Operation '{}' took {:?} (expected < 1s)",
481 self.operation, elapsed
482 ),
483 timestamp: SystemTime::now()
484 .duration_since(UNIX_EPOCH)
485 .unwrap()
486 .as_secs(),
487 metadata: Some(format!("elapsed_ms={}", elapsed.as_millis())),
488 });
489 }
490
491 *self.last_op.write() = Instant::now();
493
494 None
495 }
496}
497
498impl Drop for TimingGuard {
499 fn drop(&mut self) {
500 if !self.completed {
501 }
504 }
505}
506
507pub struct TimingAnomalyDetector {
513 pub max_duration: Duration,
515 pub min_duration: Duration,
517}
518
519impl Default for TimingAnomalyDetector {
520 fn default() -> Self {
521 TimingAnomalyDetector {
522 max_duration: Duration::from_secs(1),
523 min_duration: Duration::from_micros(100),
524 }
525 }
526}
527
528impl TimingAnomalyDetector {
529 pub fn check(&self, duration: Duration) -> Option<AnomalyEvent> {
531 let severity = if duration > self.max_duration * 10 {
532 Severity::Critical
533 } else if duration > self.max_duration {
534 Severity::Error
535 } else if duration < self.min_duration {
536 Severity::Warning
538 } else {
539 return None;
540 };
541
542 Some(AnomalyEvent {
543 anomaly_type: AnomalyType::TimingAnomaly,
544 severity,
545 description: format!("Timing anomaly: {:?}", duration),
546 timestamp: SystemTime::now()
547 .duration_since(UNIX_EPOCH)
548 .unwrap()
549 .as_secs(),
550 metadata: Some(format!("duration_us={}", duration.as_micros())),
551 })
552 }
553}
554
555#[cfg(test)]
560mod tests {
561 use super::*;
562 use crate::crypto::SoftwareKeyStore;
563
564 #[test]
565 fn test_panic_protected_keystore() {
566 let inner = SoftwareKeyStore::generate().unwrap();
567 let protected = PanicProtectedKeyStore::new(inner, 5);
568
569 let sig = protected.sign(b"test").unwrap();
571 assert!(!sig.is_empty());
572 }
573
574 #[test]
575 fn test_panic_destroys_keys() {
576 let inner = SoftwareKeyStore::generate().unwrap();
577 let protected = PanicProtectedKeyStore::new(inner, 5);
578
579 protected.emergency_panic("Test panic");
581
582 assert!(protected.is_destroyed());
584 assert_eq!(protected.state(), PanicState::Destroyed);
585
586 assert!(protected.sign(b"test").is_err());
588 }
589
590 #[test]
591 fn test_anomaly_threshold() {
592 let inner = SoftwareKeyStore::generate().unwrap();
593 let protected = PanicProtectedKeyStore::new(inner, 3);
594
595 for _ in 0..2 {
597 protected.report_anomaly(AnomalyEvent {
598 anomaly_type: AnomalyType::TimingAnomaly,
599 severity: Severity::Error,
600 description: "Test".into(),
601 timestamp: 0,
602 metadata: None,
603 });
604 }
605
606 assert!(!protected.is_destroyed());
608
609 protected.report_anomaly(AnomalyEvent {
611 anomaly_type: AnomalyType::TimingAnomaly,
612 severity: Severity::Error,
613 description: "Test".into(),
614 timestamp: 0,
615 metadata: None,
616 });
617
618 assert!(protected.is_destroyed());
619 }
620
621 #[test]
622 fn test_critical_immediate_panic() {
623 let inner = SoftwareKeyStore::generate().unwrap();
624 let protected = PanicProtectedKeyStore::new(inner, 100);
625
626 protected.report_anomaly(AnomalyEvent {
628 anomaly_type: AnomalyType::CryptoFailure,
629 severity: Severity::Critical,
630 description: "Critical test".into(),
631 timestamp: 0,
632 metadata: None,
633 });
634
635 assert!(protected.is_destroyed());
636 }
637
638 #[test]
639 fn test_panic_log() {
640 let inner = SoftwareKeyStore::generate().unwrap();
641 let protected = PanicProtectedKeyStore::new(inner, 5);
642
643 protected.emergency_panic("Test");
644
645 let log = protected.get_panic_log();
646 assert_eq!(log.len(), 1);
647 assert_eq!(log[0].to_state, PanicState::Destroyed);
648 }
649
650 #[test]
651 fn test_timing_detector() {
652 let detector = TimingAnomalyDetector::default();
653
654 assert!(detector.check(Duration::from_millis(100)).is_none());
656
657 assert!(detector.check(Duration::from_secs(5)).is_some());
659
660 assert!(detector.check(Duration::from_nanos(1)).is_some());
662 }
663}