_hope_core/
panic_integrity.rs

1//! # Hope Genome v1.8.0 - Panic Integrity (Self-Destructing Protection)
2//!
3//! **THE BLACK BOX** - If compromised, destroy everything
4//!
5//! ## Problem
6//!
7//! Even with perfect software security, physical attacks exist:
8//! - **Cold Boot Attack**: Freeze RAM, read keys from memory
9//! - **Rowhammer**: Bit-flip attacks via DRAM physics
10//! - **DMA Attack**: Direct memory access via Thunderbolt/PCIe
11//! - **Side-Channel**: Timing, power analysis, EM emanation
12//!
13//! ## Solution: Self-Destructing Integrity
14//!
15//! ```text
16//! ┌─────────────────────────────────────────────────────────────────┐
17//! │                    PANIC INTEGRITY SYSTEM                        │
18//! │                                                                  │
19//! │   ┌─────────────────────────────────────────────────────────┐   │
20//! │   │                  ANOMALY DETECTORS                       │   │
21//! │   │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │   │
22//! │   │  │Timing   │  │Memory   │  │Syscall  │  │Crypto   │     │   │
23//! │   │  │Anomaly  │  │Pressure │  │Pattern  │  │Failure  │     │   │
24//! │   │  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘     │   │
25//! │   └───────┼────────────┼────────────┼────────────┼──────────┘   │
26//! │           │            │            │            │              │
27//! │           └────────────┴─────┬──────┴────────────┘              │
28//! │                              ▼                                  │
29//! │                    ┌─────────────────┐                          │
30//! │                    │  PANIC ENGINE   │                          │
31//! │                    │   (Threshold)   │                          │
32//! │                    └────────┬────────┘                          │
33//! │                             │                                   │
34//! │              ┌──────────────┼──────────────┐                    │
35//! │              ▼              ▼              ▼                    │
36//! │      ┌────────────┐  ┌────────────┐  ┌────────────┐            │
37//! │      │  ZEROIZE   │  │  FREEZE    │  │   ALERT    │            │
38//! │      │  ALL KEYS  │  │  KEYSTORE  │  │  NETWORK   │            │
39//! │      └────────────┘  └────────────┘  └────────────┘            │
40//! │                                                                  │
41//! │   Result: Attacker gets NOTHING. Keys destroyed. Alert sent.   │
42//! └─────────────────────────────────────────────────────────────────┘
43//! ```
44//!
45//! ## Security Guarantees
46//!
47//! - **Pre-emptive**: Destroys keys BEFORE extraction possible
48//! - **Non-recoverable**: Keys are overwritten, not just freed
49//! - **Alerting**: Network notification of attack attempt
50//! - **Forensic**: Panic log for post-incident analysis
51//!
52//! ---
53//!
54//! **Date**: 2026-01-01
55//! **Version**: 1.8.0 (Betonozás Edition - Panic Integrity)
56//! **Author**: Máté Róbert <stratosoiteam@gmail.com>
57
58use 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// ============================================================================
68// PANIC TYPES
69// ============================================================================
70
71/// Anomaly type detected by the system
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub enum AnomalyType {
74    /// Timing attack detected (operation took too long/short)
75    TimingAnomaly,
76    /// Memory pressure attack (allocation failures)
77    MemoryPressure,
78    /// Suspicious syscall pattern
79    SyscallAnomaly,
80    /// Cryptographic operation failure
81    CryptoFailure,
82    /// Signature verification failed unexpectedly
83    SignatureAnomaly,
84    /// Nonce reuse detected
85    NonceReuse,
86    /// Too many failed operations
87    ExcessiveFailures,
88    /// External trigger (manual panic)
89    ExternalTrigger,
90}
91
92/// Severity level of anomaly
93#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
94pub enum Severity {
95    /// Informational, no action
96    Info = 0,
97    /// Warning, log but continue
98    Warning = 1,
99    /// Error, increment panic counter
100    Error = 2,
101    /// Critical, immediate panic
102    Critical = 3,
103}
104
105/// Anomaly event
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct AnomalyEvent {
108    /// Anomaly type
109    pub anomaly_type: AnomalyType,
110    /// Severity
111    pub severity: Severity,
112    /// Description
113    pub description: String,
114    /// Timestamp
115    pub timestamp: u64,
116    /// Additional data
117    pub metadata: Option<String>,
118}
119
120/// Panic state
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
122pub enum PanicState {
123    /// Normal operation
124    Normal,
125    /// Elevated threat level
126    Elevated,
127    /// Keys frozen (no new operations)
128    Frozen,
129    /// Keys destroyed (terminal state)
130    Destroyed,
131}
132
133/// Panic log entry
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct PanicLogEntry {
136    /// Entry ID
137    pub id: u64,
138    /// Timestamp
139    pub timestamp: u64,
140    /// State transition
141    pub from_state: PanicState,
142    pub to_state: PanicState,
143    /// Triggering anomaly
144    pub trigger: AnomalyEvent,
145    /// Hash chain (for tamper evidence)
146    pub prev_hash: [u8; 32],
147    pub entry_hash: [u8; 32],
148}
149
150// ============================================================================
151// PROTECTED KEY STORE
152// ============================================================================
153
154/// Key material that can be destroyed
155#[allow(dead_code)]
156#[derive(Zeroize)]
157#[zeroize(drop)]
158struct DestructibleKey {
159    /// The actual key bytes
160    key_bytes: Vec<u8>,
161    /// Canary value (if changed, memory was tampered)
162    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        // Canary should not be all zeros (indicates tampering/corruption)
176        !self.canary.iter().all(|&b| b == 0)
177    }
178
179    fn destroy(&mut self) {
180        // Overwrite with random data first
181        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        // Then zero
185        self.key_bytes.zeroize();
186        self.canary.zeroize();
187    }
188}
189
190/// Protected KeyStore wrapper with panic capability
191#[allow(clippy::type_complexity)]
192pub struct PanicProtectedKeyStore<K: KeyStore> {
193    /// Inner keystore
194    inner: Arc<RwLock<Option<K>>>,
195
196    /// Panic state
197    state: Arc<RwLock<PanicState>>,
198
199    /// Anomaly counter
200    anomaly_count: AtomicU64,
201
202    /// Panic threshold
203    panic_threshold: u64,
204
205    /// Is frozen
206    frozen: AtomicBool,
207
208    /// Is destroyed
209    destroyed: AtomicBool,
210
211    /// Panic log
212    panic_log: Arc<RwLock<Vec<PanicLogEntry>>>,
213
214    /// Alert callback
215    alert_callback: Option<Box<dyn Fn(&PanicLogEntry) + Send + Sync>>,
216
217    /// Last operation timestamp (for timing analysis)
218    last_op_time: Arc<RwLock<Instant>>,
219}
220
221impl<K: KeyStore> PanicProtectedKeyStore<K> {
222    /// Create new panic-protected keystore
223    ///
224    /// # Arguments
225    ///
226    /// * `inner` - The actual keystore to protect
227    /// * `panic_threshold` - Number of anomalies before panic
228    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    /// Set alert callback for panic events
243    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    /// Report anomaly
251    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                // Immediate panic
257                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    /// Trigger panic - destroy all keys
273    fn trigger_panic(&self, trigger: AnomalyEvent) {
274        let old_state = *self.state.read();
275
276        // Already destroyed?
277        if old_state == PanicState::Destroyed {
278            return;
279        }
280
281        // Freeze first
282        self.frozen.store(true, Ordering::SeqCst);
283        *self.state.write() = PanicState::Frozen;
284
285        // Log the panic
286        let log_entry = self.create_log_entry(old_state, PanicState::Destroyed, trigger);
287
288        // Alert if callback set
289        if let Some(ref callback) = self.alert_callback {
290            callback(&log_entry);
291        }
292
293        // Add to log
294        self.panic_log.write().push(log_entry);
295
296        // DESTROY THE KEYS
297        if let Some(ref mut _keystore) = *self.inner.write() {
298            // The keystore will be dropped, which should zeroize if properly implemented
299        }
300        *self.inner.write() = None;
301
302        // Mark as destroyed
303        self.destroyed.store(true, Ordering::SeqCst);
304        *self.state.write() = PanicState::Destroyed;
305    }
306
307    /// Create log entry
308    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        // Compute entry hash
324        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    /// Log anomaly without triggering panic
343    fn log_anomaly(&self, _anomaly: AnomalyEvent) {
344        // In production, this would write to secure log
345    }
346
347    /// Check timing for anomaly detection
348    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    /// Get current state
360    pub fn state(&self) -> PanicState {
361        *self.state.read()
362    }
363
364    /// Is keystore frozen?
365    pub fn is_frozen(&self) -> bool {
366        self.frozen.load(Ordering::SeqCst)
367    }
368
369    /// Is keystore destroyed?
370    pub fn is_destroyed(&self) -> bool {
371        self.destroyed.load(Ordering::SeqCst)
372    }
373
374    /// Get anomaly count
375    pub fn anomaly_count(&self) -> u64 {
376        self.anomaly_count.load(Ordering::SeqCst)
377    }
378
379    /// Get panic log
380    pub fn get_panic_log(&self) -> Vec<PanicLogEntry> {
381        self.panic_log.read().clone()
382    }
383
384    /// Manual panic trigger (for testing or emergency)
385    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
399// Implement KeyStore for PanicProtectedKeyStore
400impl<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        // Verify is allowed even in frozen state (for audit)
422        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
446// ============================================================================
447// TIMING GUARD
448// ============================================================================
449
450/// Guard for timing-based anomaly detection
451pub 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    /// Complete timing check
469    pub fn complete(mut self) -> Option<AnomalyEvent> {
470        self.completed = true;
471        let elapsed = self.start.elapsed();
472
473        // Check for timing anomalies
474        // Operations should not take more than 1 second
475        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        // Update last op time
492        *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            // Operation was interrupted - potential attack
502            // In production, this would trigger anomaly reporting
503        }
504    }
505}
506
507// ============================================================================
508// ANOMALY DETECTORS
509// ============================================================================
510
511/// Timing anomaly detector configuration
512pub struct TimingAnomalyDetector {
513    /// Maximum allowed operation time
514    pub max_duration: Duration,
515    /// Minimum expected operation time (too fast = suspicious)
516    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    /// Check if duration is anomalous
530    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            // Too fast could indicate cached/bypassed operation
537            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// ============================================================================
556// TESTS
557// ============================================================================
558
559#[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        // Should work normally
570        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        // Trigger emergency panic
580        protected.emergency_panic("Test panic");
581
582        // Should be destroyed
583        assert!(protected.is_destroyed());
584        assert_eq!(protected.state(), PanicState::Destroyed);
585
586        // Operations should fail
587        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        // Report anomalies below threshold
596        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        // Should still work
607        assert!(!protected.is_destroyed());
608
609        // Third anomaly triggers panic
610        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        // Critical should trigger immediate panic
627        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        // Normal timing
655        assert!(detector.check(Duration::from_millis(100)).is_none());
656
657        // Too slow
658        assert!(detector.check(Duration::from_secs(5)).is_some());
659
660        // Too fast
661        assert!(detector.check(Duration::from_nanos(1)).is_some());
662    }
663}