Skip to main content

oxiz_proof/
pcc.rs

1//! Proof-Carrying Code (PCC) generation
2//!
3//! This module provides utilities for generating proof-carrying code,
4//! which is code accompanied by a formal proof that it satisfies certain
5//! safety or correctness properties.
6//!
7//! ## Overview
8//!
9//! Proof-carrying code allows producers of code to provide a proof
10//! that the code satisfies specified security or safety properties.
11//! Consumers can verify this proof without trusting the producer.
12//!
13//! ## References
14//!
15//! - Necula, G.C. (1997). "Proof-Carrying Code"
16//! - Appel, A.W. (2001). "Foundational Proof-Carrying Code"
17
18use crate::proof::{Proof, ProofNodeId};
19use std::collections::HashMap;
20use std::fmt;
21
22/// A safety property that code must satisfy
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum SafetyProperty {
25    /// Memory safety (no buffer overflows, use-after-free, etc.)
26    MemorySafety,
27    /// Type safety (well-typed operations)
28    TypeSafety,
29    /// Control flow integrity
30    ControlFlowIntegrity,
31    /// Resource bounds (e.g., bounded memory/time usage)
32    ResourceBounds {
33        memory: Option<usize>,
34        time: Option<usize>,
35    },
36    /// Custom property with a name and description
37    Custom { name: String, description: String },
38}
39
40impl fmt::Display for SafetyProperty {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Self::MemorySafety => write!(f, "MemorySafety"),
44            Self::TypeSafety => write!(f, "TypeSafety"),
45            Self::ControlFlowIntegrity => write!(f, "ControlFlowIntegrity"),
46            Self::ResourceBounds { memory, time } => {
47                write!(f, "ResourceBounds(memory=")?;
48                if let Some(m) = memory {
49                    write!(f, "{}", m)?;
50                } else {
51                    write!(f, "∞")?;
52                }
53                write!(f, ", time=")?;
54                if let Some(t) = time {
55                    write!(f, "{}", t)?;
56                } else {
57                    write!(f, "∞")?;
58                }
59                write!(f, ")")
60            }
61            Self::Custom { name, .. } => write!(f, "Custom({})", name),
62        }
63    }
64}
65
66/// A verification condition (VC) that must be proven
67#[derive(Debug, Clone)]
68pub struct VerificationCondition {
69    /// Unique identifier for this VC
70    pub id: String,
71    /// The property being verified
72    pub property: SafetyProperty,
73    /// The condition that must be proven (as a formula)
74    pub condition: String,
75    /// Program point where this VC applies
76    pub location: CodeLocation,
77}
78
79/// Location in the code where a verification condition applies
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct CodeLocation {
82    /// Function or procedure name
83    pub function: String,
84    /// Basic block or statement label
85    pub label: Option<String>,
86    /// Line number (if available)
87    pub line: Option<usize>,
88}
89
90impl fmt::Display for CodeLocation {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(f, "{}", self.function)?;
93        if let Some(label) = &self.label {
94            write!(f, "::{}", label)?;
95        }
96        if let Some(line) = self.line {
97            write!(f, " (line {})", line)?;
98        }
99        Ok(())
100    }
101}
102
103/// Proof-carrying code certificate
104///
105/// This combines code with proofs of its safety properties.
106#[derive(Debug)]
107pub struct ProofCarryingCode {
108    /// The code (or reference to it)
109    code: String,
110    /// Safety properties that are certified
111    properties: Vec<SafetyProperty>,
112    /// Verification conditions
113    vcs: Vec<VerificationCondition>,
114    /// Proofs for each verification condition
115    vc_proofs: HashMap<String, ProofNodeId>,
116    /// The underlying proof structure
117    proof: Proof,
118}
119
120impl ProofCarryingCode {
121    /// Create a new proof-carrying code certificate
122    #[must_use]
123    pub fn new(code: impl Into<String>) -> Self {
124        Self {
125            code: code.into(),
126            properties: Vec::new(),
127            vcs: Vec::new(),
128            vc_proofs: HashMap::new(),
129            proof: Proof::new(),
130        }
131    }
132
133    /// Add a safety property to be verified
134    pub fn add_property(&mut self, property: SafetyProperty) {
135        self.properties.push(property);
136    }
137
138    /// Add a verification condition
139    pub fn add_vc(&mut self, vc: VerificationCondition) {
140        self.vcs.push(vc);
141    }
142
143    /// Attach a proof for a verification condition
144    pub fn attach_proof(&mut self, vc_id: &str, proof_node: ProofNodeId) {
145        self.vc_proofs.insert(vc_id.to_string(), proof_node);
146    }
147
148    /// Get the underlying proof structure
149    #[must_use]
150    pub fn proof(&self) -> &Proof {
151        &self.proof
152    }
153
154    /// Get a mutable reference to the proof
155    pub fn proof_mut(&mut self) -> &mut Proof {
156        &mut self.proof
157    }
158
159    /// Get the code
160    #[must_use]
161    pub fn code(&self) -> &str {
162        &self.code
163    }
164
165    /// Get the certified properties
166    #[must_use]
167    pub fn properties(&self) -> &[SafetyProperty] {
168        &self.properties
169    }
170
171    /// Get the verification conditions
172    #[must_use]
173    pub fn verification_conditions(&self) -> &[VerificationCondition] {
174        &self.vcs
175    }
176
177    /// Check if all VCs have proofs
178    #[must_use]
179    pub fn is_complete(&self) -> bool {
180        self.vcs
181            .iter()
182            .all(|vc| self.vc_proofs.contains_key(&vc.id))
183    }
184
185    /// Get the number of verified VCs
186    #[must_use]
187    pub fn verified_count(&self) -> usize {
188        self.vc_proofs.len()
189    }
190
191    /// Get the total number of VCs
192    #[must_use]
193    pub fn total_vc_count(&self) -> usize {
194        self.vcs.len()
195    }
196
197    /// Generate a human-readable certificate
198    #[must_use]
199    pub fn to_certificate(&self) -> String {
200        let mut cert = String::new();
201        cert.push_str("=== Proof-Carrying Code Certificate ===\n\n");
202
203        cert.push_str("Properties:\n");
204        for prop in &self.properties {
205            cert.push_str(&format!("  - {}\n", prop));
206        }
207        cert.push('\n');
208
209        cert.push_str(&format!(
210            "Verification Status: {}/{} VCs verified\n\n",
211            self.verified_count(),
212            self.total_vc_count()
213        ));
214
215        cert.push_str("Verification Conditions:\n");
216        for vc in &self.vcs {
217            cert.push_str(&format!(
218                "  [{}] {} at {}\n",
219                vc.id, vc.property, vc.location
220            ));
221            if self.vc_proofs.contains_key(&vc.id) {
222                cert.push_str("    Status: VERIFIED ✓\n");
223            } else {
224                cert.push_str("    Status: UNVERIFIED ✗\n");
225            }
226        }
227
228        cert.push_str("\n=== End Certificate ===\n");
229        cert
230    }
231}
232
233/// Builder for creating proof-carrying code certificates
234pub struct PccBuilder {
235    pcc: ProofCarryingCode,
236}
237
238impl PccBuilder {
239    /// Create a new PCC builder
240    #[must_use]
241    pub fn new(code: impl Into<String>) -> Self {
242        Self {
243            pcc: ProofCarryingCode::new(code),
244        }
245    }
246
247    /// Add a safety property
248    pub fn with_property(mut self, property: SafetyProperty) -> Self {
249        self.pcc.add_property(property);
250        self
251    }
252
253    /// Add a verification condition
254    pub fn with_vc(mut self, vc: VerificationCondition) -> Self {
255        self.pcc.add_vc(vc);
256        self
257    }
258
259    /// Build the PCC certificate
260    #[must_use]
261    pub fn build(self) -> ProofCarryingCode {
262        self.pcc
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn test_pcc_creation() {
272        let pcc = ProofCarryingCode::new("fn safe_add(a: i32, b: i32) -> i32 { a + b }");
273        assert_eq!(pcc.total_vc_count(), 0);
274        assert_eq!(pcc.verified_count(), 0);
275        assert!(pcc.is_complete());
276    }
277
278    #[test]
279    fn test_add_property() {
280        let mut pcc = ProofCarryingCode::new("code");
281        pcc.add_property(SafetyProperty::MemorySafety);
282        assert_eq!(pcc.properties().len(), 1);
283    }
284
285    #[test]
286    fn test_add_vc() {
287        let mut pcc = ProofCarryingCode::new("code");
288        let vc = VerificationCondition {
289            id: "vc1".to_string(),
290            property: SafetyProperty::TypeSafety,
291            condition: "typeof(x) = int".to_string(),
292            location: CodeLocation {
293                function: "main".to_string(),
294                label: Some("entry".to_string()),
295                line: Some(10),
296            },
297        };
298        pcc.add_vc(vc);
299        assert_eq!(pcc.total_vc_count(), 1);
300        assert!(!pcc.is_complete());
301    }
302
303    #[test]
304    fn test_attach_proof() {
305        let mut pcc = ProofCarryingCode::new("code");
306        let vc = VerificationCondition {
307            id: "vc1".to_string(),
308            property: SafetyProperty::TypeSafety,
309            condition: "typeof(x) = int".to_string(),
310            location: CodeLocation {
311                function: "main".to_string(),
312                label: None,
313                line: Some(5),
314            },
315        };
316        pcc.add_vc(vc);
317
318        let proof_node = pcc.proof_mut().add_axiom("typeof(x) = int");
319        pcc.attach_proof("vc1", proof_node);
320
321        assert!(pcc.is_complete());
322        assert_eq!(pcc.verified_count(), 1);
323    }
324
325    #[test]
326    fn test_pcc_builder() {
327        let pcc = PccBuilder::new("safe code")
328            .with_property(SafetyProperty::MemorySafety)
329            .with_property(SafetyProperty::TypeSafety)
330            .with_vc(VerificationCondition {
331                id: "vc1".to_string(),
332                property: SafetyProperty::MemorySafety,
333                condition: "bounds_check(array, index)".to_string(),
334                location: CodeLocation {
335                    function: "access".to_string(),
336                    label: None,
337                    line: Some(15),
338                },
339            })
340            .build();
341
342        assert_eq!(pcc.properties().len(), 2);
343        assert_eq!(pcc.total_vc_count(), 1);
344    }
345
346    #[test]
347    fn test_certificate_generation() {
348        let mut pcc = ProofCarryingCode::new("test code");
349        pcc.add_property(SafetyProperty::MemorySafety);
350
351        let cert = pcc.to_certificate();
352        assert!(cert.contains("Proof-Carrying Code Certificate"));
353        assert!(cert.contains("MemorySafety"));
354    }
355
356    #[test]
357    fn test_resource_bounds_display() {
358        let prop = SafetyProperty::ResourceBounds {
359            memory: Some(1024),
360            time: Some(100),
361        };
362        let s = format!("{}", prop);
363        assert!(s.contains("1024"));
364        assert!(s.contains("100"));
365    }
366
367    #[test]
368    fn test_code_location_display() {
369        let loc = CodeLocation {
370            function: "process".to_string(),
371            label: Some("loop_head".to_string()),
372            line: Some(42),
373        };
374        let s = format!("{}", loc);
375        assert!(s.contains("process"));
376        assert!(s.contains("loop_head"));
377        assert!(s.contains("42"));
378    }
379}