provenance/tests/slsa_dwarf_release.rs
//! Holger's provenance scanner verifies a release bundle from dwarves, the build server,
//! in its SLSA Build L3 release-build mode (dwarves-slsa): a DSSE envelope, key-signed (ECDSA P-256, as the
//! owner's YubiKey signs), over one in-toto v1 / SLSA Provenance v1 statement for a
//! whole release. The policy is the one docs/SLSA.md gives operators.
use holger_provenance::{effective, verify_artifact, ProvenanceFloor, ProvenanceGate, Verdict};
const DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/slsa-dwarf");
const BUILDER: &str = "https://git.gunnar.rs/nordisk/dwarves/slsa/builders/dwarves-server/v1";
const KEYID: &str = "b7238cc7bd98598c053314a119a98e670bc3ab1243cf3f7bf35817a7e3f4b6a5";
fn read(name: &str) -> Vec<u8> {
std::fs::read(format!("{DIR}/{name}")).unwrap()
}
/// The operator's RON (docs/SLSA.md): the release key on the floor, the dwarves builder at
/// SLSA build level 3 and the key on the source's gate.
fn policy(level: u8, slsa_min: u8) -> holger_provenance::EffectivePolicy {
let pem = String::from_utf8(read("release-test-key.pub")).unwrap();
let floor: ProvenanceFloor = ron::from_str(&format!(
r#"(keys: [(id: "{KEYID}", alg: ecdsa_p256, public_key: "{}")])"#,
pem.replace('\n', "\\n")
))
.unwrap();
let gate: ProvenanceGate =
ron::from_str(&format!(r#"require(builders: [(id: "{BUILDER}", level: {level})], keys: ["{KEYID}"], slsa_min: {slsa_min})"#)).unwrap();
effective(&floor, &gate)
}
fn bundle() -> Vec<(String, Vec<u8>)> {
vec![(
"holger-2.0.0-rebuild.intoto.jsonl".to_string(),
read("holger-2.0.0-rebuild.intoto.jsonl"),
)]
}
#[test]
fn a_crate_of_a_dwarves_release_passes_at_slsa_build_level_3() {
let r = verify_artifact(
&read("holger-errcode-2.0.0.crate"),
&bundle(),
&policy(3, 3),
None,
);
assert_eq!(r.verdict, Verdict::Pass, "{}", r.detail);
let c = r
.verified_provenance()
.next()
.expect("a verified provenance candidate");
assert_eq!(c.builder_id.as_deref(), Some(BUILDER));
assert_eq!(c.slsa_level, Some(3));
let slsa = c.slsa.as_ref().unwrap();
assert_eq!(
slsa.build_type,
"https://git.gunnar.rs/nordisk/dwarves/slsa/buildtypes/cargo-release/v1"
);
assert_eq!(slsa.external_parameters["release"], "holger-2.0.0-rebuild");
assert!(slsa
.resolved_dependencies
.iter()
.any(|d| d["digest"]["gitCommit"] == "87e5ada50ab5745464e4b46305bfd62d0460f52c"));
}
#[test]
fn a_changed_crate_is_refused() {
let mut bytes = read("holger-errcode-2.0.0.crate");
let n = bytes.len();
bytes[n - 1] ^= 1;
let r = verify_artifact(&bytes, &bundle(), &policy(3, 3), None);
assert_eq!(r.verdict, Verdict::Block, "{}", r.detail);
}
#[test]
fn without_the_builder_at_level_3_the_minimum_refuses_it() {
// A builder the policy names without a level counts as a signed builder (L2).
let pem = String::from_utf8(read("release-test-key.pub")).unwrap();
let floor: ProvenanceFloor = ron::from_str(&format!(
r#"(keys: [(id: "{KEYID}", alg: ecdsa_p256, public_key: "{}")])"#,
pem.replace('\n', "\\n")
))
.unwrap();
let gate: ProvenanceGate = ron::from_str(&format!(
r#"require(builders: ["{BUILDER}"], keys: ["{KEYID}"], slsa_min: 3)"#
))
.unwrap();
let r = verify_artifact(
&read("holger-errcode-2.0.0.crate"),
&bundle(),
&effective(&floor, &gate),
None,
);
assert_eq!(r.verdict, Verdict::Block, "{}", r.detail);
assert_eq!(
r.code.as_deref(),
Some("holger-provenance-7"),
"{}",
r.detail
);
}
#[test]
fn another_key_is_refused() {
let other = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYMc989J2nOEa5vxSMSfKIxhMSv6hulsvWVx8o36/jGzQtXVXeopc2v+lPihUGW/PhKT+BpLPxM7EP4Ayo/6NRw==\n-----END PUBLIC KEY-----\n";
let floor: ProvenanceFloor = ron::from_str(&format!(
r#"(keys: [(id: "{KEYID}", alg: ecdsa_p256, public_key: "{}")])"#,
other.replace('\n', "\\n")
))
.unwrap();
let gate: ProvenanceGate = ron::from_str(&format!(
r#"require(builders: [(id: "{BUILDER}", level: 3)], keys: ["{KEYID}"], slsa_min: 3)"#
))
.unwrap();
let r = verify_artifact(
&read("holger-errcode-2.0.0.crate"),
&bundle(),
&effective(&floor, &gate),
None,
);
assert_eq!(r.verdict, Verdict::Block, "{}", r.detail);
// Refused for the signature, not for an unreadable key.
assert_eq!(
r.code.as_deref(),
Some("holger-provenance-3"),
"{}",
r.detail
);
}