tpm2-store/tests/integration_test.rs
Patrick Uiterwijk cf65a6e208
Re-enable policy test
Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
2021-12-08 15:24:45 +01:00

191 lines
6 KiB
Rust

use std::{io::Write, os::unix::process::CommandExt, process::Command};
use anyhow::{bail, Context, Result};
type CheckFunction = dyn Fn(&str) -> Result<()>;
struct EncryptFunc {
func: Box<dyn Fn(&str, &str) -> Result<String>>,
name: &'static str,
}
struct DecryptFunc {
func: Box<dyn Fn(&str) -> Result<String>>,
name: &'static str,
}
const EXENAME: &str = env!("CARGO_BIN_EXE_clevis-pin-tpm2");
const CONFIG_STRINGS: &[(&str, &CheckFunction)] = &[
// No sealing
(r#"{}"#, &always_success),
// No sealing, RSA
(r#"{"key": "rsa"}"#, &always_success),
// No sealing with sha1 name alg
(r#"{"hash": "sha1"}"#, &always_success),
// Sealed against PCR23
(r#"{"pcr_ids": [23]}"#, &always_success),
// sealed against SHA1 PCR23
(r#"{"pcr_bank": "sha1", "pcr_ids": [23]}"#, &always_success),
];
// Check functions
fn always_success(_token: &str) -> Result<()> {
Ok(())
}
fn call_cmd_and_get_output(cmd: &mut Command, input: &str) -> Result<String> {
if let Ok(val) = std::env::var("TCTI") {
cmd.env("TCTI", &val);
cmd.env("TPM2TOOLS_TCTI", &val);
}
let mut child = cmd
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.context("Failed to spawn process")?;
child
.stdin
.as_mut()
.unwrap()
.write_all(input.as_bytes())
.context("Failed to write input")?;
let output = child
.wait_with_output()
.context("Failed to wait for process")?;
if !output.status.success() {
bail!("Command failed: {:?}", cmd);
}
Ok(String::from_utf8(output.stdout)?)
}
// Encrypt/Decrypt functions
fn generate_encrypt_us(renamed: bool) -> EncryptFunc {
EncryptFunc {
name: if renamed { "us_renamed" } else { "us" },
func: Box::new(move |plaintext: &str, config: &str| -> Result<String> {
let mut cmd = Command::new(EXENAME);
call_cmd_and_get_output(
if renamed {
cmd.arg0("clevis-encrypt-tpm2plus").arg(config)
} else {
cmd.arg("encrypt").arg(config)
},
plaintext,
)
}),
}
}
fn generate_decrypt_us(renamed: bool) -> DecryptFunc {
DecryptFunc {
name: if renamed { "us_renamed" } else { "us" },
func: Box::new(move |input: &str| -> Result<String> {
let mut cmd = Command::new(EXENAME);
call_cmd_and_get_output(
if renamed {
cmd.arg0("clevis-decrypt-tpm2plus")
} else {
cmd.arg("decrypt")
},
input,
)
}),
}
}
fn generate_encrypt_clevis() -> EncryptFunc {
EncryptFunc {
name: "clevis",
func: Box::new(move |plaintext: &str, config: &str| -> Result<String> {
call_cmd_and_get_output(
Command::new("clevis")
.arg("encrypt")
.arg("tpm2")
.arg(config),
plaintext,
)
}),
}
}
fn generate_decrypt_clevis() -> DecryptFunc {
DecryptFunc {
name: "clevis",
func: Box::new(move |input: &str| -> Result<String> {
call_cmd_and_get_output(Command::new("clevis").arg("decrypt"), input)
}),
}
}
const INPUT: &str = "some-static-content";
const FAIL_FAST: Option<&'static str> = option_env!("FAIL_FAST");
const SKIP_CLEVIS: Option<&'static str> = option_env!("SKIP_CLEVIS");
// Testing against clevis requires https://github.com/latchset/clevis/commit/c6fc63fc055c18927decc7bcaa07821d5ae37614
#[test]
fn pcr_tests() {
let mut encrypters = vec![generate_encrypt_us(false), generate_encrypt_us(true)];
let mut decrypters = vec![generate_decrypt_us(false), generate_decrypt_us(true)];
if SKIP_CLEVIS.is_none() {
encrypters.push(generate_encrypt_clevis());
decrypters.push(generate_decrypt_clevis());
}
let mut failed: u64 = 0;
for (config, checker) in CONFIG_STRINGS {
for encrypt_fn in &encrypters {
for decrypt_fn in &decrypters {
if encrypt_fn.name == decrypt_fn.name && encrypt_fn.name == "clevis" {
// This is a boring case we're not interested in
continue;
}
if failed != 0 && FAIL_FAST.is_some() {
panic!("At least one test failed, and fail-fast enabled");
}
eprintln!(
"Executing with encrypt: {}, decrypt: {}, config: '{}'",
encrypt_fn.name, decrypt_fn.name, config,
);
eprintln!("\tStarting encrypter");
let encrypted = (encrypt_fn.func)(INPUT, config);
if let Err(e) = encrypted {
eprintln!("FAILED: error: {:?}", e);
failed += 1;
continue;
}
let encrypted = encrypted.unwrap();
eprintln!("\tStarting checker");
if let Err(e) = checker(&encrypted) {
eprintln!("FAILED: error: {:?}", e);
failed += 1;
continue;
}
eprintln!("\tStarting decrypter");
let decrypted = (decrypt_fn.func)(&encrypted);
if let Err(e) = decrypted {
eprintln!("FAILED: error: {:?}", e);
failed += 1;
continue;
}
let decrypted = decrypted.unwrap();
eprintln!("\tStarting contents checker");
if decrypted != INPUT {
eprintln!("FAILED: '{}' (input) != '{}' (decrypted)", INPUT, decrypted);
failed += 1;
continue;
}
eprintln!("\tPASSED");
}
}
}
if failed != 0 {
panic!("{} tests failed", failed);
}
}