|
@@ -960,6 +960,13 @@ mod test {
|
|
|
use tempdir::TempDir;
|
|
|
use std::{
|
|
|
fs::File,
|
|
|
+ process::{
|
|
|
+ Child,
|
|
|
+ Command,
|
|
|
+ ExitStatus,
|
|
|
+ Stdio
|
|
|
+ },
|
|
|
+ sync::atomic::{AtomicU16, Ordering},
|
|
|
};
|
|
|
use tss_esapi::{
|
|
|
interface_types::{
|
|
@@ -974,15 +981,99 @@ mod test {
|
|
|
};
|
|
|
use ctor::ctor;
|
|
|
|
|
|
- trait TestContextExt {
|
|
|
- fn for_test() -> Result<Context>;
|
|
|
+ trait ExitStatusExt {
|
|
|
+ fn success_or_err(&self) -> Result<()>;
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ExitStatusExt for ExitStatus {
|
|
|
+ fn success_or_err(&self) -> Result<()> {
|
|
|
+ match self.success() {
|
|
|
+ true => Ok(()),
|
|
|
+ false => Err(Error::custom("ExitCode was not successful")),
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- impl TestContextExt for Context {
|
|
|
- fn for_test() -> Result<Context> {
|
|
|
- let config = TabrmdConfig::from_str("bus_type=session").conv_err()?;
|
|
|
+ struct SwtpmHarness {
|
|
|
+ dir: TempDir,
|
|
|
+ swtpm: Child,
|
|
|
+ abrmd: Child,
|
|
|
+ bus_name: String,
|
|
|
+ state_path: PathBuf,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl SwtpmHarness {
|
|
|
+ fn new() -> Result<SwtpmHarness> {
|
|
|
+ static PORT: AtomicU16 = AtomicU16::new(21901);
|
|
|
+ let port = PORT.fetch_add(2, Ordering::SeqCst);
|
|
|
+ let ctrl_port = port + 1;
|
|
|
+ let dir = TempDir::new(format!("btnode.{port}").as_str()).conv_err()?;
|
|
|
+ let dir_path = dir.path();
|
|
|
+ let dir_path_display = dir_path.display();
|
|
|
+ let bus_name = format!("btnode.port{port}");
|
|
|
+ let conf_path = dir_path.join("swtpm_setup.conf");
|
|
|
+ let state_path = dir_path.join("state.bt");
|
|
|
+ let addr = "127.0.0.1";
|
|
|
+ std::fs::write(&conf_path,
|
|
|
+r#"# Program invoked for creating certificates
|
|
|
+create_certs_tool= /usr/bin/swtpm_localca
|
|
|
+# Comma-separated list (no spaces) of PCR banks to activate by default
|
|
|
+active_pcr_banks = sha256
|
|
|
+"#)?;
|
|
|
+ Command::new("swtpm_setup")
|
|
|
+ .args([
|
|
|
+ "--tpm2",
|
|
|
+ "--config", conf_path.to_str().unwrap(),
|
|
|
+ "--tpm-state", format!("dir://{dir_path_display}").as_str(),
|
|
|
+ ])
|
|
|
+ .stdout(Stdio::null())
|
|
|
+ .status()?
|
|
|
+ .success_or_err()?;
|
|
|
+ let swtpm = Command::new("swtpm")
|
|
|
+ .args([
|
|
|
+ "socket",
|
|
|
+ "--tpm2",
|
|
|
+ "--server", format!("type=tcp,port={port},bindaddr={addr}").as_str(),
|
|
|
+ "--ctrl", format!("type=tcp,port={ctrl_port},bindaddr={addr}").as_str(),
|
|
|
+ "--log", format!("file={dir_path_display}/log.txt,level=5").as_str(),
|
|
|
+ "--flags", "not-need-init,startup-clear",
|
|
|
+ "--tpmstate", format!("dir={dir_path_display}").as_str(),
|
|
|
+ ])
|
|
|
+ .spawn()?;
|
|
|
+ let abrmd = Command::new("tpm2-abrmd")
|
|
|
+ .args([
|
|
|
+ "--session",
|
|
|
+ format!("--dbus-name={bus_name}").as_str(),
|
|
|
+ format!("--tcti=swtpm:host={addr},port={port}").as_str(),
|
|
|
+ ])
|
|
|
+ .spawn()?;
|
|
|
+ std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
+ Ok(SwtpmHarness { dir, swtpm, abrmd, bus_name, state_path })
|
|
|
+ }
|
|
|
+
|
|
|
+ fn context(&self) -> Result<Context> {
|
|
|
+ let config_string = format!("bus_type=session,bus_name={}", self.bus_name);
|
|
|
+ let config = TabrmdConfig::from_str(config_string.as_str()).conv_err()?;
|
|
|
Context::new(TctiNameConf::Tabrmd(config)).conv_err()
|
|
|
}
|
|
|
+
|
|
|
+ fn state_path(&self) -> &Path {
|
|
|
+ &self.state_path
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl Drop for SwtpmHarness {
|
|
|
+ fn drop(&mut self) {
|
|
|
+ if let Err(error) = self.abrmd.kill() {
|
|
|
+ error!("failed to kill tpm2-abrmd process with PID {}: {}", self.abrmd.id(), error);
|
|
|
+ }
|
|
|
+ if let Err(error) = self.abrmd.wait() {
|
|
|
+ error!("failed to wait for abrmd with PID {} to halt: {}", self.abrmd.id(), error);
|
|
|
+ }
|
|
|
+ if let Err(error) = self.swtpm.kill() {
|
|
|
+ error!("failed to kill swtpm process with PID {}: {}", self.swtpm.id(), error);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[ctor]
|
|
@@ -1000,13 +1091,14 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn create_context() {
|
|
|
- let mut context = Context::for_test().unwrap();
|
|
|
- context.self_test(true).unwrap();
|
|
|
+ let harness = SwtpmHarness::new().unwrap();
|
|
|
+ harness.context().unwrap().self_test(true).unwrap();
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn create_primary_key() {
|
|
|
- let mut context = Context::for_test().unwrap();
|
|
|
+ let harness = SwtpmHarness::new().unwrap();
|
|
|
+ let mut context = harness.context().unwrap();
|
|
|
|
|
|
let public = {
|
|
|
let object_attributes = ObjectAttributes::builder()
|
|
@@ -1069,9 +1161,10 @@ mod test {
|
|
|
/// Tests that a TPM Credential Store can be created when a cookie does not already exist.
|
|
|
#[test]
|
|
|
fn tpm_cred_store_new() -> Result<()> {
|
|
|
+ let harness = SwtpmHarness::new()?;
|
|
|
let dir = TempDir::new("btnode").conv_err()?;
|
|
|
let cookie_path = dir.path().join("cookie.bin");
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, &cookie_path)?;
|
|
|
let cookie = File::open(&cookie_path).conv_err()?;
|
|
|
let metadata = cookie.metadata().conv_err()?;
|
|
|
let actual = metadata.permissions().mode();
|
|
@@ -1084,9 +1177,10 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn gen_creds() -> Result<()> {
|
|
|
+ let harness = SwtpmHarness::new()?;
|
|
|
let dir = TempDir::new("btnode").conv_err()?;
|
|
|
let cookie_path = dir.path().join("cookie.bin");
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, &cookie_path)?;
|
|
|
store.gen_node_creds()?;
|
|
|
Ok(())
|
|
|
}
|
|
@@ -1123,15 +1217,12 @@ mod test {
|
|
|
assert_eq(MessageDigest::sha3_512(), Nid::sha3_512());
|
|
|
}
|
|
|
|
|
|
- fn state_path<P: AsRef<Path>>(dir: P) -> PathBuf {
|
|
|
- dir.as_ref().join("state.bt")
|
|
|
- }
|
|
|
-
|
|
|
- fn test_store() -> Result<(TpmCredStore, TempDir)> {
|
|
|
- let dir = TempDir::new("btnode").conv_err()?;
|
|
|
- let state_path = state_path(dir.path());
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, &state_path)?;
|
|
|
- Ok((store, dir))
|
|
|
+ /// Returns a SwtpmHarness and a TpmCredStore that uses it. Note that the order of the entries
|
|
|
+ /// in the returned tuple is significant, as TpmCredStore must be dropped _before_ SwtpmHarness.
|
|
|
+ fn test_store() -> Result<(SwtpmHarness, TpmCredStore)> {
|
|
|
+ let harness = SwtpmHarness::new()?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, &harness.state_path())?;
|
|
|
+ Ok((harness, store))
|
|
|
}
|
|
|
|
|
|
fn sign_verify_test(creds: &TpmCreds) -> Result<()> {
|
|
@@ -1148,7 +1239,7 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn tpm_sign_verify() -> Result<()> {
|
|
|
- let (store, _dir) = test_store()?;
|
|
|
+ let (_harness, store) = test_store()?;
|
|
|
let creds = store.gen_node_creds()?;
|
|
|
sign_verify_test(&creds)
|
|
|
}
|
|
@@ -1167,7 +1258,7 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn tpm_encrypt_decrypt() -> Result<()> {
|
|
|
- let (store, _dir) = test_store()?;
|
|
|
+ let (_harness, store) = test_store()?;
|
|
|
let creds = store.gen_node_creds()?;
|
|
|
encrypt_decrypt_test(&creds)
|
|
|
}
|
|
@@ -1192,20 +1283,22 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn persistent_handles() {
|
|
|
- let mut context = Context::for_test().unwrap();
|
|
|
+ let harness = SwtpmHarness::new().unwrap();
|
|
|
+ let mut context = harness.context().unwrap();
|
|
|
let _all_handles = context.persistent_handles().unwrap();
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn first_free_persistent() -> Result<()> {
|
|
|
- let mut context = Context::for_test()?;
|
|
|
+ let harness = SwtpmHarness::new()?;
|
|
|
+ let mut context = harness.context()?;
|
|
|
let _first = context.unused_persistent_primary_key()?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn persist_key() -> Result<()> {
|
|
|
- let (store, _dir) = test_store()?;
|
|
|
+ let (_harness, store) = test_store()?;
|
|
|
let cookie = Cookie::random()?;
|
|
|
let params = KeyParams::with_unique(cookie.as_slice());
|
|
|
let pair = store.gen_key(params)?;
|
|
@@ -1217,14 +1310,14 @@ mod test {
|
|
|
/// Tests that the same node key is returned by after a cred store is dropped and recreated.
|
|
|
#[test]
|
|
|
fn node_key_persisted() -> Result<()> {
|
|
|
- let (store, dir) = test_store()?;
|
|
|
+ let (harness, store) = test_store()?;
|
|
|
let expected = {
|
|
|
let creds = store.node_creds()?;
|
|
|
// This contains a hash of the public keys in `creds`, so it strongly identifies them.
|
|
|
creds.owner()
|
|
|
};
|
|
|
drop(store);
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, state_path(dir.path()))?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
|
|
|
let creds = store.node_creds()?;
|
|
|
let actual = creds.owner();
|
|
|
assert_eq!(expected, actual);
|
|
@@ -1236,13 +1329,13 @@ mod test {
|
|
|
#[test]
|
|
|
fn root_key_persisted() -> Result<()> {
|
|
|
const PASSWORD: &str = "Scaramouch";
|
|
|
- let (store, dir) = test_store()?;
|
|
|
+ let (harness, store) = test_store()?;
|
|
|
let expected = {
|
|
|
let creds = store.gen_root_creds(PASSWORD)?;
|
|
|
creds.owner()
|
|
|
};
|
|
|
drop(store);
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, state_path(dir.path()))?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
|
|
|
let creds = store.root_creds(PASSWORD)?;
|
|
|
let actual = creds.owner();
|
|
|
assert_eq!(expected, actual);
|
|
@@ -1253,10 +1346,10 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn root_key_not_returned_when_password_wrong() -> Result<()> {
|
|
|
- let (store, dir) = test_store()?;
|
|
|
+ let (harness, store) = test_store()?;
|
|
|
store.gen_root_creds("Galileo")?;
|
|
|
drop(store);
|
|
|
- let store = TpmCredStore::new(Context::for_test()?, state_path(dir.path()))?;
|
|
|
+ let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
|
|
|
let creds = store.root_creds("Figaro")?;
|
|
|
assert!(sign_verify_test(&creds).is_err());
|
|
|
assert!(encrypt_decrypt_test(&creds).is_err());
|