crates/dwarves-slsa/tests/release_key_attestation.rs
//! The release key's YubiKey attestation (`keys/release-signing-key.attestation.pem`,
//! the same files as `slsa/` in holger and gunnar): the attestation certificate chains to
//! the YubiKey's PIV attestation CA ("Yubico PIV Attestation B 1"), and the key it attests
//! is the built-in release key. A different key does not match the attestation.
use std::path::{Path, PathBuf};
use std::process::Command;
use dwarves_slsa::dsse;
use dwarves_slsa::release_key;
fn key_file(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("keys")
.join(name)
}
/// `openssl x509 <flag> -noout -in <cert>`.
fn x509(cert: &Path, flag: &str) -> String {
let out = Command::new("openssl")
.args(["x509", flag, "-noout", "-in"])
.arg(cert)
.output()
.expect("openssl is needed for this test");
assert!(out.status.success(), "openssl x509 {flag} failed");
String::from_utf8(out.stdout).unwrap()
}
/// The SPKI DER of the (P-256) public key in an X.509 certificate (PEM).
fn attested_spki(cert: &Path) -> Vec<u8> {
dsse::spki_from_pem(&x509(cert, "-pubkey")).unwrap()
}
#[test]
fn the_attestation_chains_to_the_yubikey_attestation_ca() {
let out = Command::new("openssl")
.args(["verify", "-partial_chain", "-CAfile"])
.arg(key_file("yubikey-piv-attestation-ca.pem"))
.arg(key_file("release-signing-key.attestation.pem"))
.output()
.expect("openssl is needed for this test");
let text = String::from_utf8_lossy(&out.stdout);
assert!(
out.status.success() && text.trim_end().ends_with(": OK"),
"{text}"
);
// The leaf attests slot 9c; the intermediate is the device's own attestation
// certificate (slot f9), issued by Yubico's PIV attestation CA.
let leaf = key_file("release-signing-key.attestation.pem");
let ca = key_file("yubikey-piv-attestation-ca.pem");
assert!(x509(&leaf, "-subject").contains("YubiKey PIV Attestation 9c"));
assert!(x509(&leaf, "-issuer").contains("YubiKey PIV Attestation"));
assert!(x509(&ca, "-issuer").contains("Yubico PIV Attestation B 1"));
}
#[test]
fn the_attested_key_is_the_release_key() {
let attested = attested_spki(&key_file("release-signing-key.attestation.pem"));
assert_eq!(attested, release_key::spki().unwrap());
assert_eq!(dsse::keyid(&attested), release_key::RELEASE_KEY_ID);
}
#[test]
fn a_different_key_does_not_match_the_attestation() {
let attested = attested_spki(&key_file("release-signing-key.attestation.pem"));
let other =
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/openssl/release-test.pub");
let other = dsse::spki_from_pem(&std::fs::read_to_string(other).unwrap()).unwrap();
assert_ne!(attested, other);
assert_ne!(dsse::keyid(&other), release_key::RELEASE_KEY_ID);
// The CA is not the release key either: only the attested leaf is.
let ca = x509(&key_file("yubikey-piv-attestation-ca.pem"), "-pubkey");
assert_ne!(ca.trim(), release_key::RELEASE_KEY_PEM.trim());
}
#[test]
fn a_certificate_not_from_the_yubikey_ca_is_refused() {
let d = tempfile::tempdir().unwrap();
let key = d.path().join("k.pem");
let cert = d.path().join("c.pem");
assert!(Command::new("openssl")
.args([
"req",
"-x509",
"-newkey",
"ec",
"-pkeyopt",
"ec_paramgen_curve:prime256v1",
"-nodes",
"-days",
"1",
"-subj",
"/CN=not-a-yubikey",
"-keyout"
])
.arg(&key)
.arg("-out")
.arg(&cert)
.output()
.unwrap()
.status
.success());
let out = Command::new("openssl")
.args(["verify", "-partial_chain", "-CAfile"])
.arg(key_file("yubikey-piv-attestation-ca.pem"))
.arg(&cert)
.output()
.unwrap();
assert!(!out.status.success());
assert_ne!(attested_spki(&cert), release_key::spki().unwrap());
}