plugin/host/tests/the_forge_signed_guests_load.rs
//! **The modules Build Thing built and signed load in this host, answer, and are
//! refused by name the moment their provenance is wrong.**
//!
//! `an_unsigned_module_stops_the_boot.rs` proves the trust gate over a WAT probe
//! signed by a test CA. That leaves the half that matters on a box unmeasured:
//! do the REAL guests — `dwarves buildthing ./holger --suite MVP --variant
//! plugin-*`, built in the pinned toolchain container and signed by the forge
//! (`sign = { purpose = "plugin" }`) — carry a `.p7s` this host accepts, a
//! `.blake3` that is the digest the host computes, and do they answer once
//! loaded?
//!
//! Where the modules are read from: `HOLGER_SIGNED_PLUGIN_DIR`, else the forge's
//! own output path `<repo>/target/wasm32-unknown-unknown/release`. A module
//! there with NO `.p7s` beside it is a host build (`cargo xtask
//! build-wasm-plugins`), not the forge's, and fails with the command that makes
//! the right one — it never skips.
//!
//! The owner's trust root is not in this repository, so the one test that needs
//! it reads `HOLGER_PLUGIN_TRUST_ROOT` (a DER certificate — the root the
//! plugin-signing certificate is issued under) and **skips BY NAME** without
//! it. Every refusal test runs unconditionally: a stranger's root, a stripped
//! signature and a signature moved onto another module need no owner key.
//!
//! The forge needs only the OPEN tree beside holger (no monetize-impl, no
//! holger-ui: the sold row's edge is lazy and holger-ui is the front row's own
//! sibling). On dwarf the forge CLI is `~/.local/bin/dwarves`, built from
//! nordisk/dwarves main in the toolchain container with
//! `cargo build --release -p dwarves --bin dwarves --no-default-features --features
//! cli,build-image,wasm-stage,runs-on-container,container-oci,sign-step`
//! (`sign-step` is the signing; the default `embed-nornir` needs libgraphblas).
//!
//! ```text
//! dwarves buildthing ./holger --suite MVP --variant plugin-allocation # and the other two
//! HOLGER_PLUGIN_TRUST_ROOT=~/.dwarves/root/korp-root-ca.der \
//! cargo test -p holger-plugin-host --features wasm-plugins,verify-modules,meter-guests \
//! --test the_forge_signed_guests_load -- --nocapture
//! ```
use std::path::{Path, PathBuf};
use std::sync::Arc;
use holger_plugin_abi::v2::Capability;
use holger_plugin_host::backend::SharedStore;
use holger_plugin_host::meter::MeterRuntime;
use holger_plugin_host::verify::ModuleTrust;
use holger_plugin_host::{
DirStore, HandlerBackend, MeterLoadError, NoServices, PluginPolicy, WasmHandler, WasmMeter,
};
use traits::{ArtifactId, RepositoryBackendTrait};
use znippy_common::sign::{dev, ArchiveSigner};
/// `(variant, module file, what the loaded guest must call itself)`.
const HANDLERS: &[(&str, &str, &str)] = &[
(
"plugin-skidbladnir",
"holger_handler_skidbladnir_wasm.wasm",
"skidbladnir",
),
(
"plugin-rust-toolchain",
"holger_handler_rust_toolchain_wasm.wasm",
"rust-toolchain",
),
];
const METER: (&str, &str) = ("plugin-allocation", "holger_plugin_allocation_wasm.wasm");
const METER_GRANT: &[Capability] = &[Capability::UsageRead, Capability::StoreList];
fn all_modules() -> Vec<(&'static str, &'static str)> {
let mut v: Vec<_> = HANDLERS.iter().map(|(var, m, _)| (*var, *m)).collect();
v.push(METER);
v
}
fn signed_dir() -> PathBuf {
std::env::var("HOLGER_SIGNED_PLUGIN_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../target/wasm32-unknown-unknown/release")
})
}
/// The forge's module, with both sidecars, or a panic carrying the build command.
fn forge_module(variant: &str, file: &str) -> PathBuf {
let p = signed_dir().join(file);
let fix = format!(
"Build it through Build Thing (signed, in the pinned container):\n \
dwarves buildthing ./holger --suite MVP --variant {variant}"
);
assert!(
p.is_file(),
"{} is missing. This test loads the REAL signed module; it does not skip.\n{fix}",
p.display()
);
let sig = ModuleTrust::signature_path(&p);
assert!(
sig.is_file(),
"{} has no {} beside it — that is a host build, not the forge's.\n{fix}",
p.display(),
sig.display()
);
assert!(
p.with_file_name(format!("{file}.blake3")).is_file(),
"{} has no .blake3 beside it.\n{fix}",
p.display()
);
p
}
fn scratch(name: &str) -> PathBuf {
let p = std::env::temp_dir().join(format!(
"holger-forge-signed-{name}-{}-{:?}",
std::process::id(),
std::thread::current().id()
));
let _ = std::fs::remove_dir_all(&p);
std::fs::create_dir_all(&p).expect("scratch dir");
p
}
/// Copy a module (and, when asked, its `.p7s`) into a fresh directory.
fn staged(name: &str, module: &Path, with_signature: bool) -> PathBuf {
let dir = scratch(name);
let dst = dir.join(module.file_name().unwrap());
std::fs::copy(module, &dst).expect("copy module");
if with_signature {
std::fs::copy(
ModuleTrust::signature_path(module),
ModuleTrust::signature_path(&dst),
)
.expect("copy signature");
}
dst
}
fn store(name: &str) -> SharedStore {
Arc::new(DirStore::new(scratch(name)))
}
/// Load a module of either kind under `policy`: the refusal string, or the
/// verified signer and digest.
fn load(
file: &str,
path: &Path,
policy: &PluginPolicy,
) -> Result<(Option<String>, String), String> {
if file == METER.1 {
WasmMeter::load_with(
path,
store("meter"),
Arc::new(NoServices),
policy,
METER_GRANT,
)
.map(|m| (m.signer().map(str::to_string), m.digest_hex().to_string()))
.map_err(|e| match e {
MeterLoadError::Refused { module, reason } => format!("{module}: {reason}"),
other => format!("{other:?}"),
})
} else {
WasmHandler::load_with(path, store("handler"), policy)
.map(|h| (h.signer().map(str::to_string), h.digest_hex().to_string()))
}
}
/// A policy that requires trust in a freshly minted stranger's root.
fn strangers_policy(dir: &Path, der: &[u8]) -> PluginPolicy {
let root = dir.join("stranger-root.der");
std::fs::write(&root, der).expect("write root");
PluginPolicy::new().with_trust(ModuleTrust::required([root]))
}
/// **THE positive half**: under the owner's root every forge-signed guest
/// verifies, the host's digest is the forge's `.blake3`, and the guest answers.
#[test]
fn every_forge_signed_guest_verifies_under_the_owner_root_and_answers() {
let Ok(root) = std::env::var("HOLGER_PLUGIN_TRUST_ROOT") else {
println!(
"SKIPPED BY NAME [forge-signed-guests-load]: set HOLGER_PLUGIN_TRUST_ROOT to the DER \
root the plugin-signing certificate is issued under to measure this"
);
return;
};
let policy = PluginPolicy::new().with_trust(ModuleTrust::required([PathBuf::from(&root)]));
for (variant, file) in all_modules() {
let module = forge_module(variant, file);
let (signer, digest) = load(file, &module, &policy)
.unwrap_or_else(|e| panic!("{variant}: the forge-signed module was refused: {e}"));
let signer = signer
.unwrap_or_else(|| panic!("{variant}: trust was required and no signer was reported"));
let sidecar = std::fs::read_to_string(module.with_file_name(format!("{file}.blake3")))
.expect("read .blake3");
assert_eq!(
digest,
sidecar.trim(),
"{variant}: the host's digest is not the forge's .blake3"
);
println!("MEASURED: {variant} {file} verified, signer '{signer}', blake3 {digest}");
}
// …and each one ANSWERS once loaded — a call into the guest, not only a load.
for (variant, file, name) in HANDLERS {
let module = forge_module(variant, file);
let h = WasmHandler::load_with(&module, store("answer"), &policy).expect("loads");
assert_eq!(
h.manifest().handler,
*name,
"{variant}: the guest calls itself something else"
);
let backend = HandlerBackend::new("bundles", h.into_runtime()).expect("wraps as a backend");
let id = ArtifactId {
namespace: None,
name: "nothing-here".into(),
version: "0".into(),
};
assert_eq!(
backend
.fetch(&id)
.expect("the guest answered fetch with an error"),
None
);
println!(
"MEASURED: {variant} answered manifest ({name}) and fetch (None on an empty store)"
);
}
let module = forge_module(METER.0, METER.1);
let m = WasmMeter::load_with(
&module,
store("answer-meter"),
Arc::new(NoServices),
&policy,
METER_GRANT,
)
.unwrap_or_else(|e| panic!("{}: {e:?}", METER.0));
let declared = m.meters().expect("the meter guest answered `meters`");
assert!(
!declared.is_empty(),
"the open meter guest declares its storage meters"
);
println!(
"MEASURED: {} answered `meters` with {} declaration(s)",
METER.0,
declared.len()
);
}
/// The forge's signature is a perfectly valid signature — over a root this box
/// never trusted. Refused under `holger-plugin-7`, naming the module.
#[test]
fn a_forge_signed_guest_under_a_strangers_root_is_refused_by_name() {
for (variant, file) in all_modules() {
let module = staged(
&format!("stranger-{variant}"),
&forge_module(variant, file),
true,
);
let (_, der) = dev::mint_ca("a stranger's CA").expect("mint CA");
let policy = strangers_policy(module.parent().unwrap(), &der);
let err = load(file, &module, &policy)
.expect_err("a signature chaining to an untrusted root passed");
assert!(
err.contains(holger_errcode::PLUGIN_SIGNATURE_INVALID.code),
"{variant}: {err}"
);
assert!(
err.contains(file),
"{variant}: the refusal does not name the module: {err}"
);
}
}
/// The same bytes with the `.p7s` stripped: `holger-plugin-6`, naming the
/// sidecar the operator must supply — never a module quietly loaded.
#[test]
fn a_forge_built_guest_without_its_signature_is_refused_by_name() {
for (variant, file) in all_modules() {
let module = staged(
&format!("unsigned-{variant}"),
&forge_module(variant, file),
false,
);
let (_, der) = dev::mint_ca("a stranger's CA").expect("mint CA");
let policy = strangers_policy(module.parent().unwrap(), &der);
let err = load(file, &module, &policy).expect_err("an unsigned module loaded");
assert!(
err.contains(holger_errcode::PLUGIN_MODULE_UNSIGNED.code),
"{variant}: {err}"
);
assert!(
err.contains(&format!("{file}.p7s")),
"{variant}: the refusal does not name the sidecar: {err}"
);
}
}
/// A signature by a TRUSTED key, but over another module's bytes — moved onto
/// this one. `holger-plugin-7`: the signature is checked, not counted.
#[test]
fn a_trusted_signature_over_another_guest_is_refused_by_name() {
let mods = all_modules();
for (i, (variant, file)) in mods.iter().enumerate() {
let (other_variant, other_file) = mods[(i + 1) % mods.len()];
let other_bytes =
std::fs::read(forge_module(other_variant, other_file)).expect("read the other module");
let module = staged(
&format!("moved-{variant}"),
&forge_module(variant, file),
false,
);
let (key, der) = dev::mint_ca("a stranger's CA").expect("mint CA");
let policy = strangers_policy(module.parent().unwrap(), &der);
// The policy's own root signs — but the OTHER module's digest.
let signer = dev::new_p256_signer(&key, &der, "trusted publisher").expect("signer");
let digest: [u8; 32] = *znippy_common::blake3::hash(&other_bytes).as_bytes();
std::fs::write(
ModuleTrust::signature_path(&module),
signer.sign_digest(&digest).expect("sign"),
)
.expect("write the moved signature");
let err = load(file, &module, &policy)
.expect_err("a signature over another module's bytes passed");
assert!(
err.contains(holger_errcode::PLUGIN_SIGNATURE_INVALID.code),
"{variant}: {err}"
);
assert!(
err.contains(file),
"{variant}: the refusal does not name the module: {err}"
);
}
}