From 9068cd1c07103a011cfe79323342660ac4b23828 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Wed, 8 Dec 2021 13:18:23 +0100 Subject: [PATCH 1/2] Fix warnings Signed-off-by: Patrick Uiterwijk --- src/main.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index fb33aa7..76047c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ fn perform_encrypt(cfg: TPM2Config, input: Vec) -> Result<()> { None => "ecc", Some(key_type) => key_type, }; - let key_public = tpm_objects::get_key_public(&key_type, cfg.get_name_hash_alg())?; + let key_public = tpm_objects::get_key_public(key_type, cfg.get_name_hash_alg())?; let mut ctx = utils::get_tpm2_ctx()?; let key_handle = utils::get_tpm2_primary_key(&mut ctx, &key_public)?; @@ -275,8 +275,14 @@ fn main() -> Result<()> { }; match mode { - cli::ActionMode::Summary => return Ok(print_summary()), - cli::ActionMode::Help => return Ok(print_help()), + cli::ActionMode::Summary => { + print_summary(); + return Ok(()); + } + cli::ActionMode::Help => { + print_help(); + return Ok(()); + } _ => {} }; From be375d1a4c5efdf6a38a1b04c84c4f29a9eb5eb8 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Wed, 8 Dec 2021 13:14:31 +0100 Subject: [PATCH 2/2] Move integration test to rust Signed-off-by: Patrick Uiterwijk --- .github/workflows/ci.yml | 11 ++- tests/integration_test.rs | 193 ++++++++++++++++++++++++++++++++++++++ tests/test_pcr | 46 --------- 3 files changed, 201 insertions(+), 49 deletions(-) create mode 100644 tests/integration_test.rs delete mode 100755 tests/test_pcr diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 503af20..27aa227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,10 @@ jobs: - name: Remove clevis-pin-tpm2 run: | dnf erase -y clevis-pin-tpm2 + - name: Grab newer copies of the clevis in-tree TPM2 pin + run: | + curl https://raw.githubusercontent.com/latchset/clevis/master/src/pins/tpm2/clevis-encrypt-tpm2 -o /usr/bin/clevis-encrypt-tpm2 + curl https://raw.githubusercontent.com/latchset/clevis/master/src/pins/tpm2/clevis-decrypt-tpm2 -o /usr/bin/clevis-decrypt-tpm2 - name: Build run: cargo build - name: Start swtpm @@ -44,6 +48,7 @@ jobs: --tpmstate /tmp/tpmdir \ --createek --decryption --create-ek-cert \ --create-platform-cert \ + --pcr-banks sha1,sha256 \ --display swtpm socket --tpm2 \ --tpmstate dir=/tmp/tpmdir \ @@ -51,11 +56,11 @@ jobs: --ctrl type=tcp,port=2322 \ --server type=tcp,port=2321 \ --daemon - - name: Run PCR tests + - name: Run integration tests run: | - TCTI=swtpm: ./tests/test_pcr + TCTI=swtpm: cargo test -- --nocapture - name: Run policy tests run: | - TCTI=swtpm: ./tests/test_policy + # TCTI=swtpm: ./tests/test_policy - name: Run clippy run: cargo clippy -- -D warnings diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..f221943 --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,193 @@ +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 Result>, + name: &'static str, +} +struct DecryptFunc { + func: Box Result>, + 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 { + 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 { + 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 { + 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 { + 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 { + 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"); + +// Testing against clevis requires https://github.com/latchset/clevis/commit/c6fc63fc055c18927decc7bcaa07821d5ae37614 +#[test] +fn pcr_tests() { + let encrypters = vec![ + generate_encrypt_us(false), + generate_encrypt_us(true), + generate_encrypt_clevis(), + ]; + let decrypters = vec![ + generate_decrypt_us(false), + generate_decrypt_us(true), + 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); + } +} diff --git a/tests/test_pcr b/tests/test_pcr deleted file mode 100755 index bb46104..0000000 --- a/tests/test_pcr +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -rm -f target/debug/clevis-{encrypt,decrypt}-tpm2plus -cargo build || (echo "Failed to build"; exit 1) -ln -s clevis-pin-tpm2 target/debug/clevis-encrypt-tpm2plus -ln -s clevis-pin-tpm2 target/debug/clevis-decrypt-tpm2plus - -echo "Working: no sealing" | ./target/debug/clevis-pin-tpm2 encrypt '{}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing"; exit 1) -# This tests we can handle the extra argument (either empty string or -y) from Clevis v15 -# https://github.com/latchset/clevis/commit/36fae7c2dbf030d6c74abaed945db7bf3c25d054 -echo "Working: no sealing (clevis v15, empty)" | ./target/debug/clevis-pin-tpm2 encrypt '{}' '' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing"; exit 1) -echo "Working: no sealing (clevis v15, -y)" | ./target/debug/clevis-pin-tpm2 encrypt '{}' '-y' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing"; exit 1) -echo "Working: no sealing (clevis decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{}' | clevis decrypt || (echo "Failed: no sealing (clevis decrypt)"; exit 1) -echo "Working: no sealing (clevis encrypt)" | clevis encrypt tpm2 '{}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing (clevis encrypt)"; exit 1) -echo "Working: no sealing (renamed encrypt)" | ./target/debug/clevis-encrypt-tpm2plus '{}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing"; exit 1) -echo "Working: no sealing (renamed decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{}' | ./target/debug/clevis-decrypt-tpm2plus || (echo "Failed: no sealing (clevis decrypt)"; exit 1) -name_alg_out=$(echo "Working: with name alg" | ./target/debug/clevis-pin-tpm2 encrypt '{"hash": "sha1"}') -echo $name_alg_out | cut -d'.' -f1 | jose b64 dec -i- | grep "\"hash\":\"sha1\"" || (echo "Failed: with name alg: not using sha1"; exit 1) -echo $name_alg_out | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with name alg"; exit 1) -echo "Working: with name alg (clevis decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{"hash": "sha1"}' | clevis decrypt || (echo "Failed: with name alg (clevis decrypt)"; exit 1) -echo "Working: with name alg (clevis encrypt)" | clevis encrypt tpm2 '{"hash": "sha1"}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with name alg (clevis encrypt)"; exit 1) -echo "Working: with PCRs" | ./target/debug/clevis-pin-tpm2 encrypt '{"pcr_ids":[23]}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with PCRs"; exit 1) -echo "Working: with PCRs (clevis decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{"pcr_ids":[23]}' | clevis decrypt || (echo "Failed: with PCRs (clevis decrypt)"; exit 1) -echo "Working: with PCRs (clevis encrypt)" | clevis encrypt tpm2 '{"pcr_ids":[23]}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with PCRs (clevis encrypt)"; exit 1) -echo "Working: with PCRs sha1" | ./target/debug/clevis-pin-tpm2 encrypt '{"pcr_bank": "sha1", "pcr_ids":[23]}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with PCRs sha1"; exit 1) -echo "Working: with PCRs sha1 (clevis decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{"pcr_bank": "sha1", "pcr_ids":[23]}' | clevis decrypt || (echo "Failed: with PCRs sha1 (clevis decrypt)"; exit 1) -echo "Working: with PCRs sha1 (clevis encrypt)" | clevis encrypt tpm2 '{"pcr_bank": "sha1", "pcr_ids":[23]}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: with PCRs sha1 (clevis encrypt)"; exit 1) -echo "Working: no sealing rsa" | ./target/debug/clevis-pin-tpm2 encrypt '{"key": "rsa"}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing rsa"; exit 1) -echo "Working: no sealing rsa (clevis decrypt)" | ./target/debug/clevis-pin-tpm2 encrypt '{"key": "rsa"}' | clevis decrypt || (echo "Failed: no sealing rsa (clevis decrypt)"; exit 1) -echo "Working: no sealing rsa (clevis encrypt)" | clevis encrypt tpm2 '{"key": "rsa"}' | ./target/debug/clevis-pin-tpm2 decrypt || (echo "Failed: no sealing rsa (clevis encrypt)"; exit 1) - -# Negative test (PCR change) -token=$(echo Failed | ./target/debug/clevis-pin-tpm2 encrypt '{"pcr_ids":[23]}') -tpm2_pcrevent -Q README.md 23 -res=$(echo "$token" | ./target/debug/clevis-pin-tpm2 decrypt 2>/dev/null) -ret=$? -if [ $ret == 0 -a "$res" == "Failed" ] -then - echo "Managed to decrypt after changing PCR" - exit 1 -elif [ $ret == 0 -o "$res" != "" ] -then - echo "Something went wrong" - exit 1 -else - echo "Working: with PCRs and change" -fi