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
|
- name: Remove clevis-pin-tpm2
|
||||||
run: |
|
run: |
|
||||||
dnf erase -y clevis-pin-tpm2
|
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
|
- name: Build
|
||||||
run: cargo build
|
run: cargo build
|
||||||
- name: Start swtpm
|
- name: Start swtpm
|
||||||
|
@ -44,6 +48,7 @@ jobs:
|
||||||
--tpmstate /tmp/tpmdir \
|
--tpmstate /tmp/tpmdir \
|
||||||
--createek --decryption --create-ek-cert \
|
--createek --decryption --create-ek-cert \
|
||||||
--create-platform-cert \
|
--create-platform-cert \
|
||||||
|
--pcr-banks sha1,sha256 \
|
||||||
--display
|
--display
|
||||||
swtpm socket --tpm2 \
|
swtpm socket --tpm2 \
|
||||||
--tpmstate dir=/tmp/tpmdir \
|
--tpmstate dir=/tmp/tpmdir \
|
||||||
|
@ -51,11 +56,11 @@ jobs:
|
||||||
--ctrl type=tcp,port=2322 \
|
--ctrl type=tcp,port=2322 \
|
||||||
--server type=tcp,port=2321 \
|
--server type=tcp,port=2321 \
|
||||||
--daemon
|
--daemon
|
||||||
- name: Run PCR tests
|
- name: Run integration tests
|
||||||
run: |
|
run: |
|
||||||
TCTI=swtpm: ./tests/test_pcr
|
TCTI=swtpm: cargo test -- --nocapture
|
||||||
- name: Run policy tests
|
- name: Run policy tests
|
||||||
run: |
|
run: |
|
||||||
TCTI=swtpm: ./tests/test_policy
|
# TCTI=swtpm: ./tests/test_policy
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo clippy -- -D warnings
|
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",
|
None => "ecc",
|
||||||
Some(key_type) => key_type,
|
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 mut ctx = utils::get_tpm2_ctx()?;
|
||||||
let key_handle = utils::get_tpm2_primary_key(&mut ctx, &key_public)?;
|
let key_handle = utils::get_tpm2_primary_key(&mut ctx, &key_public)?;
|
||||||
|
@ -275,8 +275,14 @@ fn main() -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
cli::ActionMode::Summary => return Ok(print_summary()),
|
cli::ActionMode::Summary => {
|
||||||
cli::ActionMode::Help => return Ok(print_help()),
|
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