guests/verify-bundle/tests/release_dist.rs
// SPDX-License-Identifier: AGPL-3.0-only
//! The release components of the verify-bundle guest: reproducible bytes
//! (no build path inside, the same module from another target directory),
//! `dist` writes exactly the components the tests load plus their blake3
//! pins, and `sign` signs them the way Holger's plugins are signed — a CMS
//! by the plugin-signing certificate under the korp root — and refuses a
//! readable key, a wrong pin, a second signing and a certificate from another
//! root, leaving no signature behind. Heimdall is a defensive sandbox.
mod common;
use common::guests::{component, core_module, release, Build, RELEASE};
use common::{ca, leaf};
use release::guest_build::build_core_in;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
fn contains(hay: &[u8], needle: &[u8]) -> bool {
hay.windows(needle.len()).any(|w| w == needle)
}
#[test]
fn the_release_modules_carry_no_build_path() {
let src = env!("CARGO_MANIFEST_DIR").as_bytes();
let tgt = env!("CARGO_TARGET_TMPDIR").as_bytes();
for b in RELEASE {
let m = core_module(b);
assert!(
!contains(m, src),
"the {b:?} module names its checkout path, so another checkout builds other bytes"
);
assert!(
!contains(m, tgt),
"the {b:?} module names its target directory"
);
let home = cargo_home();
assert!(
!contains(m, home.to_str().unwrap().as_bytes()),
"the {b:?} module names CARGO_HOME ({}), so another cargo home builds other bytes",
home.display()
);
}
}
/// The cargo home the tests' own builds use.
fn cargo_home() -> PathBuf {
std::env::var_os("CARGO_HOME")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(std::env::var_os("HOME").unwrap()).join(".cargo"))
}
/// `from` copied to `to`, without target directories and .git.
fn copy_tree(from: &Path, to: &Path) {
std::fs::create_dir_all(to).unwrap();
for e in std::fs::read_dir(from).unwrap() {
let e = e.unwrap();
let name = e.file_name();
if name == "target" || name == ".git" {
continue;
}
let ty = e.file_type().unwrap();
if ty.is_dir() {
copy_tree(&e.path(), &to.join(&name));
} else if ty.is_file() {
std::fs::copy(e.path(), to.join(&name)).unwrap();
}
}
}
/// The heimdall checkout copied to `at`; returns the guest crate inside it.
fn checkout_at(at: &Path) -> PathBuf {
let repo = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
copy_tree(&repo, at);
at.join("guests/verify-bundle")
}
#[test]
fn dist_builds_the_same_modules_from_any_checkout_cargo_home_vendored_or_not() {
// Two other checkouts at other paths, each with its own CARGO_HOME:
// (a) the registry's sources under another cargo home path (the same
// downloads, linked), (b) every dependency vendored (`cargo vendor`,
// unversioned directory names) and the registry replaced by it. Both
// must build the bytes the tests' own checkout builds, module for module.
let base = Path::new(env!("CARGO_TARGET_TMPDIR")).join("remap");
let _ = std::fs::remove_dir_all(&base);
let real_home = cargo_home();
let a = checkout_at(&base.join("a/heimdall"));
let home_a = base.join("home-a/cargo");
std::fs::create_dir_all(&home_a).unwrap();
std::os::unix::fs::symlink(real_home.join("registry"), home_a.join("registry")).unwrap();
let b = checkout_at(&base.join("bb/elsewhere/deeper/hd"));
let home_b = base.join("home-b");
std::fs::create_dir_all(&home_b).unwrap();
let vendor = base.join("vendored-sources");
let st = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.args(["vendor", "--locked", "--manifest-path"])
.arg(b.join("Cargo.toml"))
.arg(&vendor)
.stdout(std::process::Stdio::null())
.status()
.unwrap();
assert!(st.success(), "cargo vendor failed");
std::fs::write(
home_b.join("config.toml"),
format!(
"[source.crates-io]\nreplace-with = \"vendored\"\n[source.vendored]\ndirectory = \"{}\"\n",
vendor.display()
),
)
.unwrap();
for (dir, home, tgt) in [(&a, &home_a, "ta"), (&b, &home_b, "tb")] {
let env: [(&str, &OsStr); 2] = [
("CARGO_HOME", home.as_os_str()),
("CARGO_NET_OFFLINE", OsStr::new("true")),
];
for build in RELEASE {
let m = build_core_in(dir, &env, build, &base.join(tgt));
assert!(
m == core_module(build),
"the {build:?} module from {} with CARGO_HOME {} is not the tests' own",
dir.display(),
home.display()
);
}
}
let _ = std::fs::remove_dir_all(&base);
}
#[test]
fn another_target_dir_builds_the_same_job_module() {
// The job build is the one with C in it (OpenZL), where paths leak first.
let other = tempfile::tempdir().unwrap();
let again = Build::Job;
let bytes = {
let out = other.path().join("dist");
let done = release::dist(&out, &other.path().join("t")).unwrap();
assert_eq!(done.len(), 3);
std::fs::read(out.join(again.release_name().unwrap())).unwrap()
};
assert_eq!(
bytes,
component(Build::Job),
"a build in another target directory gave other bytes"
);
}
#[test]
fn dist_writes_the_components_the_tests_load_with_their_blake3() {
let out = tempfile::tempdir().unwrap();
// The same target root as the tests' own builds: nothing is rebuilt.
let done = release::dist(out.path(), Path::new(env!("CARGO_TARGET_TMPDIR"))).unwrap();
let names: Vec<&str> = done.iter().map(|a| a.name).collect();
assert_eq!(
names,
[
"heimdall-verify-bundle.wasm",
"heimdall-verify-bundle-verifier.wasm",
"heimdall-verify-bundle-decoder.wasm"
]
);
for (a, b) in done.iter().zip(RELEASE) {
let bytes = std::fs::read(&a.path).unwrap();
assert_eq!(
bytes,
component(b),
"{} is not the component the tests load",
a.name
);
assert_eq!(a.blake3, blake3::hash(&bytes).to_hex().to_string());
assert_eq!(a.sha256.len(), 64);
assert_eq!(
std::fs::read_to_string(out.path().join(format!("{}.blake3", a.name))).unwrap(),
format!("{}\n", a.blake3)
);
assert_ne!(
bytes[4..8],
[1, 0, 0, 0],
"{} is a core module, not a component",
a.name
);
}
}
/// A korp-shaped PKI in `dir`: root CA, plugin-signing leaf + its PKCS#8
/// key (mode `mode`), as `dwarves sign-cert issue --purpose plugin` lays it.
fn material(dir: &Path, mode: u32) {
let root = ca("korp root (test)");
let plugin = leaf("nordisk plugin signing", &root);
std::fs::write(dir.join(release::ROOT_CA_FILE), &root.der).unwrap();
std::fs::write(dir.join(release::PLUGIN_CERT_FILE), &plugin.cert_der).unwrap();
let key = dir.join(release::PLUGIN_KEY_FILE);
std::fs::write(&key, &plugin.key_pkcs8).unwrap();
use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(&key, std::fs::Permissions::from_mode(mode)).unwrap();
}
fn dist_copy() -> tempfile::TempDir {
let out = tempfile::tempdir().unwrap();
release::dist(out.path(), Path::new(env!("CARGO_TARGET_TMPDIR"))).unwrap();
out
}
fn p7s(dist: &Path) -> Vec<PathBuf> {
let mut v: Vec<PathBuf> = std::fs::read_dir(dist)
.unwrap()
.map(|e| e.unwrap().path())
.filter(|p| p.extension().is_some_and(|e| e == "p7s"))
.collect();
v.sort();
v
}
#[test]
fn sign_writes_three_signatures_that_the_root_verifies_and_the_host_loads() {
let m = tempfile::tempdir().unwrap();
material(m.path(), 0o600);
let d = dist_copy();
let signed = release::sign(m.path(), d.path()).unwrap();
assert_eq!(signed.len(), 3);
assert!(signed.iter().all(|s| s.signer == "nordisk plugin signing"));
assert_eq!(p7s(d.path()).len(), 3);
// Holger's side: the korp root is the host's only trust root.
let host = heimdall_wasm::Host::new(heimdall_wasm::HostConfig::new([m
.path()
.join(release::ROOT_CA_FILE)]))
.unwrap();
let job = d.path().join("heimdall-verify-bundle.wasm");
host.load_pinned(&job, &signed[0].blake3).unwrap();
// Another root does not load it.
let other = tempfile::tempdir().unwrap();
material(other.path(), 0o600);
let stranger = heimdall_wasm::Host::new(heimdall_wasm::HostConfig::new([other
.path()
.join(release::ROOT_CA_FILE)]))
.unwrap();
assert!(
stranger.load(&job).is_err(),
"a host with another root loaded the guest"
);
}
#[test]
fn sign_refuses_a_key_others_can_read() {
let m = tempfile::tempdir().unwrap();
material(m.path(), 0o644);
let d = dist_copy();
let e = release::sign(m.path(), d.path()).unwrap_err();
assert!(e.contains("0600"), "{e}");
assert!(p7s(d.path()).is_empty());
}
#[test]
fn sign_refuses_a_module_that_is_not_its_pin_and_leaves_no_signature() {
let m = tempfile::tempdir().unwrap();
material(m.path(), 0o600);
let d = dist_copy();
// The last module changed after its pin was written.
let dec = d.path().join("heimdall-verify-bundle-decoder.wasm");
let mut bytes = std::fs::read(&dec).unwrap();
let n = bytes.len() - 1;
bytes[n] ^= 1;
std::fs::write(&dec, bytes).unwrap();
let e = release::sign(m.path(), d.path()).unwrap_err();
assert!(e.contains("not the pinned"), "{e}");
assert!(
p7s(d.path()).is_empty(),
"a half-signed release was left behind"
);
}
#[test]
fn sign_refuses_to_sign_twice() {
let m = tempfile::tempdir().unwrap();
material(m.path(), 0o600);
let d = dist_copy();
release::sign(m.path(), d.path()).unwrap();
let before: Vec<Vec<u8>> = p7s(d.path())
.iter()
.map(|p| std::fs::read(p).unwrap())
.collect();
let e = release::sign(m.path(), d.path()).unwrap_err();
assert!(e.contains("signed once"), "{e}");
let after: Vec<Vec<u8>> = p7s(d.path())
.iter()
.map(|p| std::fs::read(p).unwrap())
.collect();
assert_eq!(before, after, "the first signatures were touched");
}
#[test]
fn sign_refuses_a_certificate_from_another_root_and_leaves_no_signature() {
let m = tempfile::tempdir().unwrap();
material(m.path(), 0o600);
// The certificate and key are from another PKI than korp-root-ca.der.
let other = tempfile::tempdir().unwrap();
material(other.path(), 0o600);
for f in [release::PLUGIN_CERT_FILE, release::PLUGIN_KEY_FILE] {
std::fs::copy(other.path().join(f), m.path().join(f)).unwrap();
}
let d = dist_copy();
let e = release::sign(m.path(), d.path()).unwrap_err();
assert!(e.contains("does not verify"), "{e}");
assert!(p7s(d.path()).is_empty());
}