1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! Runtime Drift Analysis
//!
//! Evaluates deviations in the running state against whitelists and rolling
//! upgrades to identify anomalies and policy violations.
use crate::digest::TypedDigest;
use alloc::string::String;
use alloc::vec::Vec;
/// Severity level of an observed runtime drift.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub enum RuntimeDriftSeverity {
/// Minor or expected deviation (e.g. non-critical config changes).
Informational,
/// Medium severity deviation (e.g. staging/rolling upgrades).
Warning,
/// Critical security anomaly (e.g. unknown kernel module, unapproved executable).
Critical,
}
/// Structured details of a runtime drift anomaly.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct RuntimeDriftReport {
/// The expected cryptographic digest for this workload path/ID.
pub expected: TypedDigest,
/// The actual cryptographic digest observed during attestation.
pub actual: TypedDigest,
/// Identification of the workload/process experiencing drift.
pub workload: String,
/// The severity level of this drift anomaly.
pub severity: RuntimeDriftSeverity,
}
/// Operational engine to analyze runtime drift.
pub struct RuntimeDriftEngine;
impl RuntimeDriftEngine {
/// Compares runtime measurements against a whitelist of approved digests
/// and a secondary list representing rolling upgrades.
///
/// - An exact match in `whitelist` produces no drift.
/// - A match in `rolling_upgrades` produces `Warning` drift.
/// - An unknown digest produces `Critical` drift.
#[must_use]
pub fn detect_drift(
actual: &[crate::runtime_attestation::RuntimeMeasurement],
whitelist: &[TypedDigest],
rolling_upgrades: &[TypedDigest],
) -> Vec<RuntimeDriftReport> {
let mut reports = Vec::new();
for measurement in actual {
let is_whitelisted = whitelist.iter().any(|d| d == &measurement.digest);
if !is_whitelisted {
let is_rolling = rolling_upgrades.iter().any(|d| d == &measurement.digest);
let severity = if is_rolling {
RuntimeDriftSeverity::Warning
} else {
RuntimeDriftSeverity::Critical
};
let expected = whitelist.first().copied().unwrap_or_else(|| {
TypedDigest::new(crate::digest::DigestAlgorithm::Sha3_256, [0; 32])
});
reports.push(RuntimeDriftReport {
expected,
actual: measurement.digest,
workload: measurement.measurement_id.clone(),
severity,
});
}
}
reports
}
}