Merge pull request #15 from puiterwijk/integrationtests
Move integration test to rust
This commit is contained in:
commit
42ba36aa71
4 changed files with 210 additions and 52 deletions
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
@ -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
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -33,7 +33,7 @@ fn perform_encrypt(cfg: TPM2Config, input: Vec<u8>) -> 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(());
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
|
|
193
tests/integration_test.rs
Normal file
193
tests/integration_test.rs
Normal file
|
@ -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<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");
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in a new issue