crates/dwarves-slsa/tests/dsse.rs
//! DSSE v1: the PAE, the envelope, and the P-256 check.
mod common;
use base64::engine::general_purpose::STANDARD as B64;
use base64::Engine as _;
use dwarves_slsa::dsse::{self, Envelope, Signature};
fn envelope(k: &common::TestKey, payload: &[u8]) -> Envelope {
let pae = dsse::pae("application/vnd.in-toto+json", payload);
Envelope {
payload_type: "application/vnd.in-toto+json".into(),
payload: B64.encode(payload),
signatures: vec![Signature {
keyid: dsse::keyid(&k.spki),
sig: B64.encode(common::sign(k, &pae)),
}],
}
}
#[test]
fn pae_is_the_dsse_spec_vector() {
assert_eq!(
dsse::pae("http://example.com/HelloWorld", b"hello world"),
b"DSSEv1 29 http://example.com/HelloWorld 11 hello world".to_vec()
);
assert_eq!(dsse::pae("", b""), b"DSSEv1 0 0 ".to_vec());
}
#[test]
fn a_signed_envelope_verifies_with_its_key() {
let k = common::key();
let env = envelope(&k, b"{\"a\":1}\n");
assert_eq!(dsse::verify_p256(&env, &k.spki).unwrap(), 0);
assert_eq!(env.payload_bytes().unwrap(), b"{\"a\":1}\n");
}
#[test]
fn a_changed_payload_is_refused() {
let k = common::key();
let mut env = envelope(&k, b"{\"a\":1}\n");
env.payload = B64.encode(b"{\"a\":2}\n");
assert!(dsse::verify_p256(&env, &k.spki).is_err());
}
#[test]
fn a_changed_payload_type_is_refused() {
let k = common::key();
let mut env = envelope(&k, b"{}");
env.payload_type = "application/json".into();
assert!(dsse::verify_p256(&env, &k.spki).is_err());
}
#[test]
fn another_key_is_refused() {
let k = common::key();
let other = common::key();
let env = envelope(&k, b"{}");
let e = dsse::verify_p256(&env, &other.spki).unwrap_err();
assert!(e.0.contains("verif"), "{e}");
}
#[test]
fn the_signature_that_verifies_is_found_among_several() {
let k = common::key();
let other = common::key();
let mut env = envelope(&other, b"{}");
env.signatures
.push(envelope(&k, b"{}").signatures.remove(0));
assert_eq!(dsse::verify_p256(&env, &k.spki).unwrap(), 1);
}
#[test]
fn an_envelope_signed_by_openssl_verifies() {
// Signed outside this crate (openssl dgst -sha256 -sign over the PAE), as a YubiKey signs.
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/openssl");
let spki = dsse::spki_from_pem(&std::fs::read_to_string(dir.join("release-test.pub")).unwrap())
.unwrap();
let bundle =
dsse::parse_bundle(&std::fs::read_to_string(dir.join("release.intoto.jsonl")).unwrap())
.unwrap();
assert_eq!(bundle.len(), 1);
assert_eq!(dsse::verify_p256(&bundle[0], &spki).unwrap(), 0);
}
#[test]
fn a_pem_public_key_reads_and_its_keyid_is_the_spki_sha256() {
let k = common::key();
let pem = format!(
"-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----\n",
B64.encode(&k.spki)
);
let spki = dsse::spki_from_pem(&pem).unwrap();
assert_eq!(spki, k.spki);
assert_eq!(
dsse::keyid(&spki),
dwarves_slsa::subjects::sha256_hex(&k.spki)
);
assert_eq!(dsse::p256_point(&spki).unwrap().len(), 65);
}
#[test]
fn a_key_that_is_not_p256_is_refused() {
// An Ed25519 SubjectPublicKeyInfo.
let ed = hex::decode("302a300506032b6570032100")
.unwrap()
.into_iter()
.chain([7u8; 32])
.collect::<Vec<u8>>();
assert!(dsse::p256_point(&ed).is_err());
assert!(dsse::p256_point(&[0u8; 10]).is_err());
assert!(
dsse::spki_from_pem("-----BEGIN PUBLIC KEY-----\n!!!\n-----END PUBLIC KEY-----\n").is_err()
);
}
#[test]
fn a_bundle_is_json_lines_or_one_pretty_envelope() {
let k = common::key();
let a = envelope(&k, b"{}");
let line = a.to_line();
assert!(line.ends_with('\n'));
assert_eq!(line.matches('\n').count(), 1);
let two = format!("{line}{line}");
assert_eq!(dsse::parse_bundle(&two).unwrap().len(), 2);
let pretty = serde_json::to_string_pretty(&a).unwrap();
assert_eq!(dsse::parse_bundle(&pretty).unwrap(), vec![a]);
assert!(dsse::parse_bundle("not json").is_err());
assert!(dsse::parse_bundle("").is_err());
}