pub mod surfaces;
pub mod policy;
pub mod attestation;
pub mod forensics;
pub mod scenario;
pub mod flake;
use std::path::{Path, PathBuf};
use std::time::Duration;
use tempfile::TempDir;
use crate::error::Result;
use surfaces::{DeterministicSurfaces, TimeMode, RngMode, FsMode, NetMode, ProcMode};
use policy::Policy;
use attestation::Attestation;
use forensics::ForensicsPack;
pub struct CleanroomCore<P: Policy> {
temp_dir: TempDir,
project_root: PathBuf,
surfaces: DeterministicSurfaces,
_policy: std::marker::PhantomData<P>,
attestation: Attestation,
forensics: ForensicsPack,
}
pub struct CleanroomBuilder<P: Policy> {
time_mode: Option<TimeMode>,
rng_mode: Option<RngMode>,
fs_mode: Option<FsMode>,
net_mode: Option<NetMode>,
proc_mode: Option<ProcMode>,
_policy: std::marker::PhantomData<P>,
}
impl<P: Policy> CleanroomCore<P> {
pub fn builder() -> CleanroomBuilder<P> {
CleanroomBuilder {
time_mode: None,
rng_mode: None,
fs_mode: None,
net_mode: None,
proc_mode: None,
_policy: std::marker::PhantomData,
}
}
pub fn root(&self) -> &Path {
&self.project_root
}
pub fn attestation(&self) -> &Attestation {
&self.attestation
}
pub fn forensics(&self) -> &ForensicsPack {
&self.forensics
}
pub fn export_attestation(&self, path: impl AsRef<Path>) -> Result<()> {
self.attestation.export(path)
}
pub fn generate_forensics_pack(&self, path: impl AsRef<Path>) -> Result<()> {
self.forensics.generate(path, &self.attestation)
}
pub fn validate_determinism<F>(&self, test_fn: F, iterations: usize) -> Result<bool>
where
F: Fn() -> Result<()>,
{
flake::validate_determinism(test_fn, iterations, &self.surfaces)
}
}
impl<P: Policy> CleanroomBuilder<P> {
pub fn time(mut self, mode: TimeMode) -> Self {
self.time_mode = Some(mode);
self
}
pub fn time_frozen(self, seed: u64) -> Self {
self.time(TimeMode::Frozen(seed))
}
pub fn rng(mut self, mode: RngMode) -> Self {
self.rng_mode = Some(mode);
self
}
pub fn rng_seeded(self, seed: u64) -> Self {
self.rng(RngMode::Seeded(seed))
}
pub fn fs(mut self, mode: FsMode) -> Self {
self.fs_mode = Some(mode);
self
}
pub fn fs_ephemeral(self) -> Self {
self.fs(FsMode::Ephemeral)
}
pub fn net(mut self, mode: NetMode) -> Self {
self.net_mode = Some(mode);
self
}
pub fn net_offline(self) -> Self {
self.net(NetMode::Offline)
}
pub fn proc(mut self, mode: ProcMode) -> Self {
self.proc_mode = Some(mode);
self
}
pub fn build(self) -> Result<CleanroomCore<P>> {
let temp_dir = TempDir::new()?;
let project_root = temp_dir.path().to_path_buf();
let surfaces = DeterministicSurfaces::new(
self.time_mode.unwrap_or(TimeMode::Real),
self.rng_mode.unwrap_or(RngMode::Real),
self.fs_mode.unwrap_or(FsMode::Real),
self.net_mode.unwrap_or(NetMode::Real),
self.proc_mode.unwrap_or(ProcMode::Real),
);
let attestation = Attestation::new(&surfaces, P::name());
let forensics = ForensicsPack::new(&project_root);
Ok(CleanroomCore {
temp_dir,
project_root,
surfaces,
_policy: std::marker::PhantomData,
attestation,
forensics,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cleanroom::policy::{Locked, Permissive};
#[test]
#[allow(clippy::expect_used)]
fn test_cleanroom_core_builder() {
let env = CleanroomCore::<Locked>::builder()
.time_frozen(42)
.rng_seeded(42)
.fs_ephemeral()
.net_offline()
.build()
.expect("Failed to build cleanroom");
assert!(env.root().exists());
}
#[test]
#[allow(clippy::expect_used)]
fn test_cleanroom_deterministic_surfaces() {
let env = CleanroomCore::<Locked>::builder()
.time_frozen(42)
.rng_seeded(42)
.build()
.expect("Failed to build cleanroom");
assert!(matches!(env.surfaces.time_mode(), TimeMode::Frozen(42)));
assert!(matches!(env.surfaces.rng_mode(), RngMode::Seeded(42)));
}
#[test]
#[allow(clippy::expect_used)]
fn test_cleanroom_attestation() {
let env = CleanroomCore::<Locked>::builder()
.build()
.expect("Failed to build cleanroom");
let attestation = env.attestation();
assert_eq!(attestation.policy(), "Locked");
}
}