Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "vm-attest-demo"
version = "0.1.0"
edition = "2024"
build = "build/main.rs"

[dependencies]
anyhow = "1.0.102"
Expand All @@ -21,3 +22,6 @@ thiserror = "2.0.18"
vm-attest = { git = "https://github.com/oxidecomputer/vm-attest", rev = "f2184012395b329b09395d1a63a1961a267d1166" }
vsock = "0.5.3"
x509-cert = "0.2.5"

[build-dependencies]
anyhow = "1.0.102"
13 changes: 13 additions & 0 deletions build/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use anyhow::{Context, Result};

mod test_data;

fn main() -> Result<()> {
test_data::generate().context("generate mock data for testing")?;

Ok(())
}
158 changes: 158 additions & 0 deletions build/test_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use anyhow::Result;
use anyhow::{Context, anyhow};
use std::env;
use std::fs::{self, File};
use std::io::Write;
use std::path::{self, Path, PathBuf};

/// Execute one of the `pki-playground` commands to generate part of the PKI
/// used for testing.
fn pki_gen_cmd(command: &str, cfg: &Path) -> Result<()> {
if !fs::exists(cfg).with_context(|| {
format!("failed to determin if file exists: {}", cfg.display())
})? {
return Err(anyhow!("missing PKI config file: {}", cfg.display()));
}

let mut cmd = std::process::Command::new("pki-playground");
cmd.arg("--config");
cmd.arg(cfg);
cmd.arg(command);
let output = cmd
.output()
.context("executing command \"pki-playground\"")?;

if !output.status.success() {
let stdout = String::from_utf8(output.stdout)
.context("String from pki-playground stdout")?;
println!("stdout: {stdout}");
let stderr = String::from_utf8(output.stderr)
.context("String from pki-playground stderr")?;
println!("stderr: {stderr}");

return Err(anyhow!("cmd failed: {cmd:?}"));
}

Ok(())
}

/// Execute one of the `attest-mock` commands to generate mock input data used
/// for testing.
fn attest_gen_cmd(command: &str, input: &Path, output: &str) -> Result<()> {
if !fs::exists(input).with_context(|| {
format!("failed to determin if file exists: {}", input.display())
})? {
return Err(anyhow!("missing config file: {}", input.display()));
}

// attest-mock "input" "cmd" > "output"
let mut cmd = std::process::Command::new("attest-mock");
cmd.arg(input).arg(command);
let cmd_output =
cmd.output().context("executing command \"attest-mock\"")?;

if cmd_output.status.success() {
std::fs::write(output, cmd_output.stdout).context("write {output}")
} else {
let stderr = String::from_utf8(cmd_output.stderr)
.context("String from attest-mock stderr")?;
println!("stderr: {stderr}");

Err(anyhow!("cmd failed: {cmd:?}"))
}
}

fn write_path_to_conf(mut file: &File, path: &Path, name: &str) -> Result<()> {
if !fs::exists(path).with_context(|| {
format!("checking existance of file: {}", path.display())
})? {
return Err(anyhow!("required file not present: {}", path.display()));
}

Ok(writeln!(
file,
"#[allow(dead_code)]\npub const {}: &str =\n \"{}\";\n",
name,
path.display(),
)?)
}

pub fn generate() -> Result<()> {
let cwd = env::current_dir().context("get current dir")?;
let mut cwd = path::absolute(cwd).context("current_dir to absolute")?;

// output directory where we put:
// generated test inputs
let mut out =
PathBuf::from(env::var("OUT_DIR").context("Could not get OUT_DIR")?);
env::set_current_dir(&out)
.with_context(|| format!("chdir to {}", out.display()))?;

// paths consumed by the library as const `&str`s go here
out.push("config.rs");
let config_out = File::create(&out)
.with_context(|| format!("creating {}", out.display()))?;
out.pop();

cwd.push("test-data");
cwd.push("config.kdl");
let mut pki_cfg = cwd;
// generate keys
pki_gen_cmd("generate-key-pairs", &pki_cfg)?;
out.push("test-alias.key.pem");
write_path_to_conf(&config_out, &out, "ATTESTATION_SIGNER")
.context("write variable w/ path to attestation signing key")?;
out.pop();

// generate certs
pki_gen_cmd("generate-certificates", &pki_cfg)?;
out.push("test-root.cert.pem");
write_path_to_conf(&config_out, &out, "PKI_ROOT")
.context("write PKI_ROOT const str to config.rs")?;
out.pop();

// generate cert chains
pki_gen_cmd("generate-certificate-lists", &pki_cfg)?;
pki_cfg.pop();
out.push("test-alias.certlist.pem");
write_path_to_conf(&config_out, &out, "SIGNER_PKIPATH")
.context("write variable w/ path to attestation signing key")?;
out.pop();

// generate measurement log
let mut log_cfg = pki_cfg;
log_cfg.push("log.kdl");
attest_gen_cmd("log", &log_cfg, "log.bin")?;
log_cfg.pop();

out.push("log.bin");
write_path_to_conf(&config_out, &out, "LOG")
.context("write variable w/ path to attestation signing key")?;
out.pop();

// generate the corpus of reference measurements
let mut corim_cfg = log_cfg;
corim_cfg.push("corim.kdl");
attest_gen_cmd("corim", &corim_cfg, "corim.cbor")?;
corim_cfg.pop();

out.push("corim.cbor");
write_path_to_conf(&config_out, &out, "CORIM").context(
"write variable w/ path to reference integrity measurements",
)?;
out.pop();

let mut vm_instance_cfg = corim_cfg;
vm_instance_cfg.push("vm-instance-cfg.json");
write_path_to_conf(&config_out, &vm_instance_cfg, "VM_INSTANCE_CFG")
.context(
"write variable w/ path to data attested by the InstanceRoT",
)?;
vm_instance_cfg.pop();

Ok(())
}
Loading