use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeAttestationResults {
pub capability_digest: String,
pub executor_image_digest: Option<String>,
pub bundle_sig_ok: bool,
pub provenance_ok: bool,
pub attestation_verified: bool,
pub verified_at: chrono::DateTime<chrono::Utc>,
pub verification_details: VerificationDetails,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationDetails {
pub checks: HashMap<String, bool>,
pub warnings: Vec<String>,
pub errors: Vec<String>,
pub context: HashMap<String, String>,
}
impl RuntimeAttestationResults {
pub fn new(
capability_digest: String,
executor_image_digest: Option<String>,
bundle_sig_ok: bool,
provenance_ok: bool,
attestation_verified: bool,
verification_details: VerificationDetails,
) -> Self {
Self {
capability_digest,
executor_image_digest,
bundle_sig_ok,
provenance_ok,
attestation_verified,
verified_at: chrono::Utc::now(),
verification_details,
}
}
pub fn is_fully_verified(&self) -> bool {
self.attestation_verified && self.bundle_sig_ok && self.provenance_ok
}
pub fn get_summary(&self) -> String {
format!(
"Attestation: {}, Signature: {}, Provenance: {}",
if self.attestation_verified { "✓" } else { "✗" },
if self.bundle_sig_ok { "✓" } else { "✗" },
if self.provenance_ok { "✓" } else { "✗" }
)
}
}
impl VerificationDetails {
pub fn new(
checks: HashMap<String, bool>,
warnings: Vec<String>,
errors: Vec<String>,
context: HashMap<String, String>,
) -> Self {
Self {
checks,
warnings,
errors,
context,
}
}
pub fn from_verification_result(
result: &smith_attestation::VerificationResult,
context: HashMap<String, String>,
) -> Self {
Self {
checks: result.checks.clone(),
warnings: result.warnings.clone(),
errors: result.errors.clone(),
context,
}
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn has_warnings(&self) -> bool {
!self.warnings.is_empty()
}
pub fn passed_checks_count(&self) -> usize {
self.checks.values().filter(|&&passed| passed).count()
}
pub fn failed_checks_count(&self) -> usize {
self.checks.values().filter(|&&passed| !passed).count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_runtime_attestation_results_creation() {
let details = VerificationDetails::new(
HashMap::new(),
vec!["Warning message".to_string()],
vec![],
HashMap::new(),
);
let results = RuntimeAttestationResults::new(
"digest123".to_string(),
Some("image_digest456".to_string()),
true,
true,
true,
details,
);
assert_eq!(results.capability_digest, "digest123");
assert_eq!(results.executor_image_digest, Some("image_digest456".to_string()));
assert!(results.bundle_sig_ok);
assert!(results.provenance_ok);
assert!(results.attestation_verified);
assert!(results.is_fully_verified());
}
#[test]
fn test_runtime_attestation_results_partial_failure() {
let details = VerificationDetails::new(
HashMap::new(),
vec![],
vec!["Signature failed".to_string()],
HashMap::new(),
);
let results = RuntimeAttestationResults::new(
"digest123".to_string(),
None,
false, true,
false, details,
);
assert!(!results.is_fully_verified());
assert!(results.get_summary().contains("✗"));
}
#[test]
fn test_verification_details() {
let mut checks = HashMap::new();
checks.insert("signature_valid".to_string(), true);
checks.insert("digest_valid".to_string(), false);
let details = VerificationDetails::new(
checks,
vec!["Warning".to_string()],
vec!["Error".to_string()],
HashMap::new(),
);
assert!(details.has_warnings());
assert!(details.has_errors());
assert_eq!(details.passed_checks_count(), 1);
assert_eq!(details.failed_checks_count(), 1);
}
}