provenance/examples/verify_slsa.rs
//! Verify one artifact against its `.intoto.jsonl` the way Holger's intake does —
//! the counterpart of `sign_slsa`, for a build the dwarves build door sealed.
//!
//! ```text
//! cargo run -p holger-provenance --example verify_slsa -- \
//! <artifact> <artifact.intoto.jsonl> <keyid> <spki-base64-or-pem-file> <builder-id> <builder-level> <slsa-min>
//! ```
//!
//! The policy is the RON floor's shape, built from the arguments: one trusted
//! Ed25519 key, one allowed builder at the given SLSA build level, and a
//! `gates.provenance: Require(slsa_min: N)`. Prints the verdict, the code and the
//! read SLSA facts; exits 0 on Pass, 1 otherwise.
use holger_provenance::{
effective, verify_artifact, BuilderRule, KeyAlg, ProvenanceFloor, ProvenanceGate, TrustedKey,
Verdict,
};
fn main() -> anyhow::Result<()> {
let args: Vec<String> = std::env::args().skip(1).collect();
let [artifact, envelope, keyid, public_key, builder, level, slsa_min] = args.as_slice() else {
anyhow::bail!("usage: verify_slsa <artifact> <envelope> <keyid> <spki-b64|pem-file> <builder-id> <builder-level> <slsa-min>");
};
let public_key = match std::fs::read_to_string(public_key) {
Ok(pem) => pem,
Err(_) => public_key.clone(),
};
let floor = ProvenanceFloor {
keys: vec![TrustedKey {
id: keyid.clone(),
alg: KeyAlg::Ed25519,
public_key,
}],
builders: vec![BuilderRule {
id: builder.clone(),
level: Some(level.parse()?),
}],
..Default::default()
};
let gate = ProvenanceGate::Require {
identity: None,
identities: vec![],
builders: vec![],
keys: vec![],
apk_keys: vec![],
pgp_keys: vec![],
slsa_min: Some(slsa_min.parse()?),
};
let name = std::path::Path::new(envelope)
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let r = verify_artifact(
&std::fs::read(artifact)?,
&[(name, std::fs::read(envelope)?)],
&effective(&floor, &gate),
None,
);
println!("verdict: {:?}", r.verdict);
println!("code: {}", r.code.as_deref().unwrap_or("-"));
println!("detail: {}", r.detail);
if let Some(c) = r.verified_provenance().next() {
if let Some(s) = &c.slsa {
println!(
"builder: {} · buildType: {} · slsa_level: {:?}",
s.builder_id, s.build_type, c.slsa_level
);
println!("resolvedDependencies: {}", s.resolved_dependencies.len());
}
}
std::process::exit(if r.verdict == Verdict::Pass { 0 } else { 1 });
}