#![deny(unsafe_code)]
#![deny(missing_docs)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProofStatus {
Verified,
InProgress,
NotStarted,
NotApplicable,
}
#[derive(Debug, Clone)]
pub struct SawSpec {
pub primitive: String,
pub status: ProofStatus,
pub c_function: Option<String>,
pub notes: String,
}
pub struct SawProofs {
specs: Vec<SawSpec>,
}
impl SawProofs {
#[must_use]
pub fn new() -> Self {
Self {
specs: vec![
SawSpec {
primitive: "AES-256-GCM".to_string(),
status: ProofStatus::Verified,
c_function: Some("aes_gcm_encrypt/decrypt".to_string()),
notes: "Verified by AWS-LC team via SAW".to_string(),
},
SawSpec {
primitive: "X25519".to_string(),
status: ProofStatus::Verified,
c_function: Some("X25519_keypair / X25519".to_string()),
notes: "Verified by AWS-LC team via SAW".to_string(),
},
SawSpec {
primitive: "ML-KEM-768".to_string(),
status: ProofStatus::InProgress,
c_function: Some("MLKEM768_encapsulate / MLKEM768_decapsulate".to_string()),
notes: "NIST PQC; SAW proofs being developed upstream".to_string(),
},
SawSpec {
primitive: "HKDF-SHA256".to_string(),
status: ProofStatus::Verified,
c_function: Some("HKDF_extract / HKDF_expand".to_string()),
notes: "Verified by AWS-LC team via SAW".to_string(),
},
SawSpec {
primitive: "TLS 1.3 state machine".to_string(),
status: ProofStatus::NotApplicable,
c_function: None,
notes: "Pure Rust (rustls), not applicable for SAW".to_string(),
},
],
}
}
#[must_use]
pub fn specs(&self) -> &[SawSpec] {
&self.specs
}
#[must_use]
pub fn with_status(&self, status: ProofStatus) -> Vec<&SawSpec> {
self.specs.iter().filter(|s| s.status == status).collect()
}
#[must_use]
pub fn all_verified(&self) -> bool {
self.specs
.iter()
.all(|s| s.status == ProofStatus::Verified || s.status == ProofStatus::NotApplicable)
}
#[must_use]
pub fn summary(&self) -> (usize, usize, usize, usize) {
let verified = self.specs.iter().filter(|s| s.status == ProofStatus::Verified).count();
let in_progress = self.specs.iter().filter(|s| s.status == ProofStatus::InProgress).count();
let not_started = self.specs.iter().filter(|s| s.status == ProofStatus::NotStarted).count();
let not_applicable =
self.specs.iter().filter(|s| s.status == ProofStatus::NotApplicable).count();
(verified, in_progress, not_started, not_applicable)
}
}
impl Default for SawProofs {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
mod tests {
use super::*;
#[test]
fn test_saw_proofs_registry_has_five_specs_succeeds() {
let proofs = SawProofs::new();
assert_eq!(proofs.specs().len(), 5);
}
#[test]
fn test_saw_proofs_verified_count_is_three_succeeds() {
let proofs = SawProofs::new();
let verified = proofs.with_status(ProofStatus::Verified);
assert_eq!(verified.len(), 3); }
#[test]
fn test_saw_proofs_in_progress_succeeds() {
let proofs = SawProofs::new();
let in_progress = proofs.with_status(ProofStatus::InProgress);
assert_eq!(in_progress.len(), 1); assert_eq!(in_progress[0].primitive, "ML-KEM-768");
}
#[test]
fn test_saw_proofs_not_applicable_succeeds() {
let proofs = SawProofs::new();
let na = proofs.with_status(ProofStatus::NotApplicable);
assert_eq!(na.len(), 1); }
#[test]
fn test_all_verified_false_due_to_ml_kem_succeeds() {
let proofs = SawProofs::new();
assert!(!proofs.all_verified()); }
#[test]
fn test_saw_proofs_summary_counts_are_correct() {
let proofs = SawProofs::new();
let (verified, in_progress, not_started, not_applicable) = proofs.summary();
assert_eq!(verified, 3);
assert_eq!(in_progress, 1);
assert_eq!(not_started, 0);
assert_eq!(not_applicable, 1);
}
#[test]
fn test_proof_status_eq_same_values_are_equal_succeeds() {
assert_eq!(ProofStatus::Verified, ProofStatus::Verified);
assert_ne!(ProofStatus::Verified, ProofStatus::InProgress);
}
#[test]
fn test_saw_spec_debug_contains_primitive_name_succeeds() {
let spec = SawSpec {
primitive: "test".to_string(),
status: ProofStatus::Verified,
c_function: Some("fn_test".to_string()),
notes: "notes".to_string(),
};
let debug = format!("{:?}", spec);
assert!(debug.contains("test"));
}
#[test]
fn test_saw_proofs_default_has_non_empty_specs_succeeds() {
let proofs = SawProofs::default();
assert!(!proofs.specs().is_empty());
}
}