use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum AttestationEvent {
BootVerified {
timestamp: u64,
sequence_number: u64,
event_hash: crate::digest::TypedDigest,
},
RuntimeMeasurementReceived {
timestamp: u64,
sequence_number: u64,
event_hash: crate::digest::TypedDigest,
},
RuntimeDriftDetected {
timestamp: u64,
sequence_number: u64,
event_hash: crate::digest::TypedDigest,
severity: crate::drift::DriftSeverity,
},
PolicyEpochChanged {
timestamp: u64,
sequence_number: u64,
event_hash: crate::digest::TypedDigest,
new_epoch: u64,
},
AttestationExpired {
timestamp: u64,
sequence_number: u64,
event_hash: crate::digest::TypedDigest,
},
}
impl AttestationEvent {
#[must_use]
pub fn timestamp(&self) -> u64 {
match *self {
Self::BootVerified { timestamp, .. }
| Self::RuntimeMeasurementReceived { timestamp, .. }
| Self::RuntimeDriftDetected { timestamp, .. }
| Self::PolicyEpochChanged { timestamp, .. }
| Self::AttestationExpired { timestamp, .. } => timestamp,
}
}
#[must_use]
pub fn sequence_number(&self) -> u64 {
match *self {
Self::BootVerified {
sequence_number, ..
}
| Self::RuntimeMeasurementReceived {
sequence_number, ..
}
| Self::RuntimeDriftDetected {
sequence_number, ..
}
| Self::PolicyEpochChanged {
sequence_number, ..
}
| Self::AttestationExpired {
sequence_number, ..
} => sequence_number,
}
}
#[must_use]
pub fn event_hash(&self) -> &crate::digest::TypedDigest {
match self {
Self::BootVerified { event_hash, .. }
| Self::RuntimeMeasurementReceived { event_hash, .. }
| Self::RuntimeDriftDetected { event_hash, .. }
| Self::PolicyEpochChanged { event_hash, .. }
| Self::AttestationExpired { event_hash, .. } => event_hash,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AttestationTimeline {
pub device_id: String,
pub events: Vec<AttestationEvent>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimelineValidationError {
TemporalOrderingViolation,
SequenceNumberRollback { expected: u64, got: u64 },
}
impl core::fmt::Display for TimelineValidationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TemporalOrderingViolation => {
f.write_str("timeline events are out of chronological order")
}
Self::SequenceNumberRollback { expected, got } => {
write!(
f,
"sequence number rollback detected: expected >= {expected}, got {got}"
)
}
}
}
}
impl AttestationTimeline {
#[must_use]
pub fn new(device_id: String) -> Self {
Self {
device_id,
events: Vec::new(),
}
}
#[must_use]
pub fn events(&self) -> &[AttestationEvent] {
&self.events
}
pub fn push(&mut self, event: AttestationEvent) {
self.events.push(event);
}
pub fn verify_consistency(&self) -> Result<(), TimelineValidationError> {
let mut last_timestamp = 0;
let mut last_sequence = None;
for event in &self.events {
let ts = event.timestamp();
if ts < last_timestamp {
return Err(TimelineValidationError::TemporalOrderingViolation);
}
last_timestamp = ts;
if let AttestationEvent::RuntimeMeasurementReceived {
sequence_number, ..
} = *event
{
if let Some(prev) = last_sequence {
if sequence_number <= prev {
return Err(TimelineValidationError::SequenceNumberRollback {
expected: prev + 1,
got: sequence_number,
});
}
}
last_sequence = Some(sequence_number);
}
}
Ok(())
}
}