crates/dwarves-slsa/tests/verify.rs
//! Verify a signed release bundle end to end.
mod common;
use base64::engine::general_purpose::STANDARD as B64;
use base64::Engine as _;
use dwarves_slsa::dsse::{self, Envelope, Signature};
use dwarves_slsa::statement::{self, Subject, PAYLOAD_TYPE_INTOTO};
use dwarves_slsa::subjects::sha256_hex;
use dwarves_slsa::verify::{verify_bundle, Expect};
fn bundle_of(k: &common::TestKey, st: &serde_json::Value, payload_type: &str) -> String {
let payload = statement::payload(st);
let sig = common::sign(k, &dsse::pae(payload_type, &payload));
Envelope {
payload_type: payload_type.into(),
payload: B64.encode(&payload),
signatures: vec![Signature {
keyid: dsse::keyid(&k.spki),
sig: B64.encode(sig),
}],
}
.to_line()
}
fn release(files: &[(&str, &[u8])]) -> serde_json::Value {
let subjects: Vec<Subject> = files
.iter()
.map(|(n, b)| Subject::sha256(*n, sha256_hex(b)))
.collect();
statement::statement(&subjects, &common::provenance()).unwrap()
}
const A: &[u8] = b"crate a bytes";
const B: &[u8] = b"crate b bytes";
#[test]
fn a_signed_release_verifies_and_its_artifacts_match() {
let k = common::key();
let st = release(&[("crates/a-1.0.0.crate", A), ("crates/b-1.0.0.crate", B)]);
let v = verify_bundle(
&bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO),
&k.spki,
&Expect::default(),
)
.unwrap();
assert_eq!(v.summary.release, "holger-2.0.1");
assert_eq!(v.keyid, dsse::keyid(&k.spki));
assert_eq!(
v.check_artifact("a-1.0.0.crate", A).unwrap().name,
"crates/a-1.0.0.crate"
);
assert!(v.subject_for(B).is_some());
}
#[test]
fn an_artifact_under_another_name_or_changed_is_refused() {
let k = common::key();
let st = release(&[("crates/a-1.0.0.crate", A), ("crates/b-1.0.0.crate", B)]);
let v = verify_bundle(
&bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO),
&k.spki,
&Expect::default(),
)
.unwrap();
assert!(
v.check_artifact("b-1.0.0.crate", A).is_err(),
"a's bytes as b"
);
assert!(v.check_artifact("a-1.0.0.crate", b"changed").is_err());
assert!(v.subject_for(b"changed").is_none());
}
#[test]
fn the_out_directory_must_be_exactly_the_subjects() {
let k = common::key();
let st = release(&[("crates/a-1.0.0.crate", A), ("bin/x", B)]);
let v = verify_bundle(
&bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO),
&k.spki,
&Expect::default(),
)
.unwrap();
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("crates")).unwrap();
std::fs::create_dir_all(d.path().join("bin")).unwrap();
std::fs::write(d.path().join("crates/a-1.0.0.crate"), A).unwrap();
assert!(v.check_dir(d.path()).is_err(), "bin/x missing");
std::fs::write(d.path().join("bin/x"), B).unwrap();
assert_eq!(v.check_dir(d.path()).unwrap(), 2);
std::fs::write(d.path().join("bin/extra"), B).unwrap();
assert!(v.check_dir(d.path()).is_err(), "an extra file");
std::fs::remove_file(d.path().join("bin/extra")).unwrap();
std::fs::write(d.path().join("bin/x"), b"changed").unwrap();
assert!(v.check_dir(d.path()).is_err(), "a changed file");
}
#[test]
fn another_key_is_refused() {
let k = common::key();
let st = release(&[("a", A)]);
assert!(verify_bundle(
&bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO),
&common::key().spki,
&Expect::default()
)
.is_err());
}
#[test]
fn another_builder_or_build_type_is_refused() {
let k = common::key();
let st = release(&[("a", A)]);
let b = bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO);
let e = verify_bundle(
&b,
&k.spki,
&Expect {
builder_id: "https://example.com/other".into(),
build_type: None,
runner_account: None,
},
)
.unwrap_err();
assert!(e.0.contains("builder"), "{e}");
let e = verify_bundle(
&b,
&k.spki,
&Expect {
build_type: Some("https://example.com/bt".into()),
..Expect::default()
},
)
.unwrap_err();
assert!(e.0.contains("build type"), "{e}");
}
#[test]
fn another_payload_type_or_two_envelopes_are_refused() {
let k = common::key();
let st = release(&[("a", A)]);
assert!(verify_bundle(
&bundle_of(&k, &st, "application/json"),
&k.spki,
&Expect::default()
)
.is_err());
let one = bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO);
let e = verify_bundle(&format!("{one}{one}"), &k.spki, &Expect::default()).unwrap_err();
assert!(e.0.contains("one envelope"), "{e}");
}
#[test]
fn a_slsa_v02_statement_is_refused_even_signed() {
let k = common::key();
let mut st = release(&[("a", A)]);
st["predicateType"] = "https://slsa.dev/provenance/v0.2".into();
assert!(verify_bundle(
&bundle_of(&k, &st, PAYLOAD_TYPE_INTOTO),
&k.spki,
&Expect::default()
)
.is_err());
}