diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d59bdf191..9d52accb8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,10 +24,14 @@ jobs: tests: name: Fedora tests runs-on: ubuntu-latest + container: + image: quay.io/keylime/keylime-ci:latest steps: - uses: actions/checkout@v4 + - name: Set git safe.directory for the working directory + run : git config --system --add safe.directory "$PWD" - name: Run tests - run: docker run --security-opt seccomp=tests/seccomp-profile.json -v $(pwd):/tmp/code_under_test -w /tmp/code_under_test quay.io/keylime/keylime-ci:latest dbus-run-session -- /tmp/code_under_test/tests/run.sh + run: bash tests/run.sh - uses: actions/upload-artifact@v4 with: name: tarpaulin-report diff --git a/keylime-agent/src/config.rs b/keylime-agent/src/config.rs index 32eee7c6f..d5209dd54 100644 --- a/keylime-agent/src/config.rs +++ b/keylime-agent/src/config.rs @@ -501,6 +501,7 @@ fn config_translate_keywords( &config.agent.agent_data_path, keylime_dir, DEFAULT_AGENT_DATA_PATH, + false, ); let mut ima_ml_path = config_get_file_path( @@ -508,6 +509,7 @@ fn config_translate_keywords( &config.agent.ima_ml_path, root_path, DEFAULT_IMA_ML_PATH, + false, ); let mut measuredboot_ml_path = config_get_file_path( @@ -515,6 +517,7 @@ fn config_translate_keywords( &config.agent.measuredboot_ml_path, root_path, DEFAULT_MEASUREDBOOT_ML_PATH, + false, ); let mut server_key = config_get_file_path( @@ -522,6 +525,7 @@ fn config_translate_keywords( &config.agent.server_key, keylime_dir, DEFAULT_SERVER_KEY, + false, ); let mut server_cert = config_get_file_path( @@ -529,6 +533,7 @@ fn config_translate_keywords( &config.agent.server_cert, keylime_dir, DEFAULT_SERVER_CERT, + false, ); let trusted_client_ca: String = @@ -540,6 +545,7 @@ fn config_translate_keywords( t, keylime_dir, DEFAULT_TRUSTED_CLIENT_CA, + false, ) }) .collect::>() @@ -550,6 +556,7 @@ fn config_translate_keywords( &config.agent.iak_cert, keylime_dir, DEFAULT_IAK_CERT, + true, ); let mut idevid_cert = config_get_file_path( @@ -557,6 +564,7 @@ fn config_translate_keywords( &config.agent.idevid_cert, keylime_dir, DEFAULT_IDEVID_CERT, + true, ); let ek_handle = match config.agent.ek_handle.as_ref() { @@ -630,6 +638,7 @@ fn config_translate_keywords( &config.agent.revocation_cert, keylime_dir, &format!("secure/unzipped/{DEFAULT_REVOCATION_CERT}"), + false, ); Ok(KeylimeConfig { @@ -657,7 +666,7 @@ fn config_translate_keywords( /// Expand a file path from the configuration file. /// /// If the string is set as "default", return the provided default path relative from the provided work_dir. -/// If the string is empty, use again the default value +/// If the string is empty, use the default value unless the 'leave_empty' is 'true' /// If the string is a relative path, return the path relative from the provided work_dir /// If the string is an absolute path, return the path without change. fn config_get_file_path( @@ -665,10 +674,15 @@ fn config_get_file_path( path: &str, work_dir: &Path, default: &str, + leave_empty: bool, ) -> String { match path { "default" => work_dir.join(default).display().to_string(), "" => { + if leave_empty { + return "".to_string(); + } + warn!("Empty string provided in configuration option {option}, using default {default}"); work_dir.join(default).display().to_string() } @@ -1107,7 +1121,7 @@ mod tests { let translated: Vec = list .iter() - .map(|e| config_get_file_path("test", e, workdir, default)) + .map(|e| config_get_file_path("test", e, workdir, default, false)) .collect(); assert_eq!( @@ -1122,5 +1136,13 @@ mod tests { ], translated ); + + let translated = + config_get_file_path("test", "", workdir, "default", true); + assert_eq!("", translated); + + let translated = + config_get_file_path("test", "", workdir, "default", false); + assert_eq!("/workdir/default", translated); } } diff --git a/keylime-agent/src/error.rs b/keylime-agent/src/error.rs index 6524ed6e9..caccb26a8 100644 --- a/keylime-agent/src/error.rs +++ b/keylime-agent/src/error.rs @@ -27,6 +27,10 @@ pub(crate) enum Error { Conversion(String), #[error("Configuration error")] Configuration(#[from] crate::config::KeylimeConfigError), + #[error("Device ID error")] + DeviceID(#[from] keylime::device_id::DeviceIDError), + #[error("Device ID builder error")] + DeviceIDBuilder(#[from] keylime::device_id::DeviceIDBuilderError), #[error("Reqwest error: {0}")] Reqwest(#[from] reqwest::Error), #[error("Registrar error: received {code} from {addr}")] diff --git a/keylime-agent/src/main.rs b/keylime-agent/src/main.rs index 78646c3ea..1291c9b3c 100644 --- a/keylime-agent/src/main.rs +++ b/keylime-agent/src/main.rs @@ -58,8 +58,11 @@ use futures::{ try_join, }; use keylime::{ - crypto, crypto::x509::CertificateBuilder, ima::MeasurementList, - list_parser::parse_list, tpm, + crypto::{self, x509::CertificateBuilder}, + device_id::{DeviceID, DeviceIDBuilder}, + ima::MeasurementList, + list_parser::parse_list, + tpm::{self, IAKResult, IDevIDResult}, }; use log::*; use openssl::{ @@ -324,137 +327,6 @@ async fn main() -> Result<()> { config.agent.tpm_signing_alg.as_ref(), )?; - let iak_cert: Option; - let idevid_cert: Option; - // Attempt to load the IAK and IDevID certificates - if config.agent.enable_iak_idevid { - iak_cert = match config.agent.iak_cert.as_ref() { - "" => { - debug!("The iak_cert option was not set in the configuration file"); - None - } - path => { - let iak_path = Path::new(&path); - if iak_path.exists() { - debug!( - "Loading IAK certificate from {}", - iak_path.display() - ); - let iakcert = match crypto::load_x509_der(iak_path) { - Ok(cert) => cert, - Err(error) => crypto::load_x509_pem(iak_path)?, - }; - Some(iakcert) - } else { - debug!("Can not find IAK certificate"); - None - } - } - }; - idevid_cert = match config.agent.idevid_cert.as_ref() { - "" => { - debug!("The idevid_cert option was not set in the configuration file"); - None - } - path => { - let idevid_path = Path::new(&path); - if idevid_path.exists() { - debug!( - "Loading IDevID certificate from {}", - idevid_path.display() - ); - let idevcert = match crypto::load_x509_der(idevid_path) { - Ok(cert) => cert, - Err(error) => crypto::load_x509_pem(idevid_path)?, - }; - Some(idevcert) - } else { - debug!("Can not find IDevID certificate"); - None - } - } - }; - } else { - iak_cert = None; - idevid_cert = None; - } - /// Regenerate the IAK and IDevID keys or collect and authorise persisted ones and check that the keys match the certificates that have been loaded - let (iak, idevid) = if config.agent.enable_iak_idevid { - /// Try to detect which template has been used by checking the certificate - let (asym_alg, name_alg) = tpm::get_idevid_template( - &crypto::match_cert_to_template( - &iak_cert.clone().ok_or(Error::Other( - "IAK/IDevID enabled but cert could not be used" - .to_string(), - ))?, - )?, - config.agent.iak_idevid_template.as_str(), - config.agent.iak_idevid_asymmetric_alg.as_str(), - config.agent.iak_idevid_name_alg.as_str(), - )?; - - /// IDevID recreation/collection - let idevid = if config.agent.idevid_handle.trim().is_empty() { - /// If handle is not set in config, recreate IDevID according to template - info!("Recreating IDevID."); - let regen_idev = ctx.create_idevid(asym_alg, name_alg)?; - ctx.flush_context(regen_idev.handle.into())?; - // Flush after creating to make room for AK and EK and IAK - regen_idev - } else { - info!("Collecting persisted IDevID."); - ctx.idevid_from_handle( - config.agent.idevid_handle.as_str(), - config.agent.idevid_password.as_str(), - )? - }; - /// Check that recreated/collected IDevID key matches the one in the certificate - if crypto::check_x509_key( - &idevid_cert.clone().ok_or(Error::Other( - "IAK/IDevID enabled but IDevID cert could not be used" - .to_string(), - ))?, - idevid.clone().public, - )? { - info!("IDevID matches certificate."); - } else { - error!("IDevID template does not match certificate. Check template in configuration."); - return Err(Error::Configuration(config::KeylimeConfigError::Generic("IDevID template does not match certificate. Check template in configuration.".to_string()))); - } - - /// IAK recreation/collection - let iak = if config.agent.iak_handle.trim().is_empty() { - /// If handle is not set in config, recreate IAK according to template - info!("Recreating IAK."); - ctx.create_iak(asym_alg, name_alg)? - } else { - /// If a handle has been set, try to collect from the handle - /// If there is an IAK password, add the password to the handle - info!("Collecting persisted IAK."); - ctx.iak_from_handle( - config.agent.iak_handle.as_str(), - config.agent.iak_password.as_str(), - )? - }; - /// Check that recreated/collected IAK key matches the one in the certificate - if crypto::check_x509_key( - &iak_cert.clone().ok_or(Error::Other( - "IAK/IDevID enabled but IAK cert could not be used" - .to_string(), - ))?, - iak.clone().public, - )? { - info!("IAK matches certificate."); - } else { - error!("IAK template does not match certificate. Check template in configuration."); - return Err(Error::Configuration(config::KeylimeConfigError::Generic("IAK template does not match certificate. Check template in configuration.".to_string()))); - } - - (Some(iak), Some(idevid)) - } else { - (None, None) - }; - // Gather EK values and certs let ek_result = match config.agent.ek_handle.as_ref() { "" => ctx.create_ek(tpm_encryption_alg, None)?, @@ -562,13 +434,41 @@ async fn main() -> Result<()> { info!("Agent UUID: {}", agent_uuid); - let (attest, signature) = if config.agent.enable_iak_idevid { - let qualifying_data = config.agent.uuid.as_bytes(); - let (attest, signature) = ctx.certify_credential_with_iak( - Data::try_from(qualifying_data).unwrap(), //#[allow_ci] - ak_handle, - iak.as_ref().unwrap().handle, //#[allow_ci] - )?; + // If using IAK/IDevID is enabled, obtain IAK/IDevID and respective certificates + let mut device_id = if config.agent.enable_iak_idevid { + let mut builder = DeviceIDBuilder::new() + .iak_handle(&config.agent.iak_handle) + .iak_password(&config.agent.iak_password) + .iak_default_template(config::DEFAULT_IAK_IDEVID_TEMPLATE) + .iak_template(&config.agent.iak_idevid_template) + .iak_asym_alg(&config.agent.iak_idevid_asymmetric_alg) + .iak_hash_alg(&config.agent.iak_idevid_name_alg) + .idevid_handle(&config.agent.idevid_handle) + .idevid_cert_path(&config.agent.idevid_cert) + .idevid_password(&config.agent.idevid_password) + .idevid_default_template(config::DEFAULT_IAK_IDEVID_TEMPLATE) + .idevid_template(&config.agent.iak_idevid_template) + .idevid_asym_alg(&config.agent.iak_idevid_asymmetric_alg) + .idevid_hash_alg(&config.agent.iak_idevid_name_alg); + + if !&config.agent.iak_cert.is_empty() { + builder = builder.iak_cert_path(&config.agent.iak_cert); + } + + if !&config.agent.idevid_cert.is_empty() { + builder = builder.idevid_cert_path(&config.agent.idevid_cert); + } + + Some(builder.build(&mut ctx)?) + } else { + None + }; + + let (attest, signature) = if let Some(dev_id) = &mut device_id { + let qualifying_data = Data::try_from(agent_uuid.as_bytes())?; + let (attest, signature) = + dev_id.certify(qualifying_data, ak_handle, &mut ctx)?; + info!("AK certified with IAK."); // // For debugging certify(), the following checks the generated signature @@ -705,8 +605,8 @@ async fn main() -> Result<()> { { // Request keyblob material let keyblob = if config.agent.enable_iak_idevid { - let (Some(iak), Some(idevid), Some(attest), Some(signature)) = - (iak, idevid, attest, signature) + let (Some(dev_id), Some(attest), Some(signature)) = + (&device_id, attest, signature) else { error!( "IDevID and IAK are enabled but could not be generated" @@ -725,15 +625,15 @@ async fn main() -> Result<()> { ek_result.ek_cert, &PublicBuffer::try_from(ak.public)?.marshall()?, Some( - &PublicBuffer::try_from(iak.public.clone())? + &PublicBuffer::try_from(dev_id.iak_pubkey.clone())? .marshall()?, ), Some( - &PublicBuffer::try_from(idevid.public.clone())? + &PublicBuffer::try_from(dev_id.idevid_pubkey.clone())? .marshall()?, ), - idevid_cert, - iak_cert, + dev_id.idevid_cert.clone(), + dev_id.iak_cert.clone(), Some(attest.marshall()?), Some(signature.marshall()?), mtls_cert, @@ -1090,7 +990,7 @@ mod testing { TSSError(#[from] tss_esapi::Error), } - impl<'a> Drop for QuoteData<'a> { + impl Drop for QuoteData<'_> { /// Flush the created AK when dropping fn drop(&mut self) { self.tpmcontext diff --git a/keylime/src/crypto.rs b/keylime/src/crypto.rs index 5564a3673..5c951b1cc 100644 --- a/keylime/src/crypto.rs +++ b/keylime/src/crypto.rs @@ -56,6 +56,10 @@ pub enum CryptoError { #[error("failed to create EcKey structure from public point")] ECKeyFromPublicPointError(#[source] openssl::error::ErrorStack), + /// File not found + #[error("could not find file {0}")] + FileNotFound(String), + /// Error creating file #[error("failed to create file {file}")] FSCreateError { @@ -270,6 +274,15 @@ pub fn load_x509_pem(input_cert_path: &Path) -> Result { X509::from_pem(&contents).map_err(CryptoError::X509FromPEMError) } +/// Load a X509 certificate in PEM or DER format from a given path +pub fn load_x509(path: &Path) -> Result { + if path.exists() { + load_x509_der(path).or(load_x509_pem(path)) + } else { + Err(CryptoError::FileNotFound(path.display().to_string())) + } +} + /// Load X509 certificate chain in PEM format from file fn load_x509_cert_chain( input_cert_path: &Path, @@ -368,7 +381,7 @@ pub fn hash( /// Check an x509 certificate contains a specific public key pub fn check_x509_key( cert: &X509, - tpm_key: tss_esapi::structures::Public, + tpm_key: &tss_esapi::structures::Public, ) -> Result { // Id:RSA_PSS only added in rust-openssl from v0.10.59; remove this let and use Id::RSA_PSS after update // Id taken from https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/nid.h#4039 @@ -389,7 +402,7 @@ pub fn check_x509_key( let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key) + let key = SubjectPublicKeyInfo::try_from(tpm_key.clone()) .map_err(CryptoError::SubjectPublicKeyInfoFromRSAError)?; let key_der = picky_asn1_der::to_vec(&key) .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; @@ -408,7 +421,7 @@ pub fn check_x509_key( let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key) + let key = SubjectPublicKeyInfo::try_from(tpm_key.clone()) .map_err(CryptoError::SubjectPublicKeyInfoFromRSAError)?; let key_der = picky_asn1_der::to_vec(&key) .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; @@ -427,7 +440,7 @@ pub fn check_x509_key( let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key) + let key = SubjectPublicKeyInfo::try_from(tpm_key.clone()) .map_err(CryptoError::SubjectPublicKeyInfoFromECCError)?; let key_der = picky_asn1_der::to_vec(&key) .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; diff --git a/keylime/src/crypto/x509.rs b/keylime/src/crypto/x509.rs index 37bcea592..71d57e7ae 100644 --- a/keylime/src/crypto/x509.rs +++ b/keylime/src/crypto/x509.rs @@ -75,7 +75,7 @@ impl<'a> CertificateBuilder<'a> { pub fn common_name( &'a mut self, cn: &'a str, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.common_name = Some(cn); self } @@ -88,7 +88,7 @@ impl<'a> CertificateBuilder<'a> { pub fn hash_algorithm( &'a mut self, hash_algorithm: MessageDigest, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.hash_algorithm = Some(hash_algorithm); self } @@ -102,7 +102,7 @@ impl<'a> CertificateBuilder<'a> { pub fn not_before( &'a mut self, days_from_now: u32, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.not_before = Some(days_from_now); self } @@ -115,7 +115,7 @@ impl<'a> CertificateBuilder<'a> { pub fn not_after( &'a mut self, days_from_now: u32, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.not_after = Some(days_from_now); self } @@ -131,7 +131,7 @@ impl<'a> CertificateBuilder<'a> { pub fn version( &'a mut self, version: i32, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.version = Some(version); self } @@ -144,7 +144,7 @@ impl<'a> CertificateBuilder<'a> { pub fn private_key( &'a mut self, private_key: &'a PKey, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { self.private_key = Some(private_key); self } @@ -158,7 +158,7 @@ impl<'a> CertificateBuilder<'a> { pub fn add_dns_names( &'a mut self, dns_names: Vec<&'a str>, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { match &mut self.dns_names { None => { self.dns_names = Some(dns_names); @@ -181,7 +181,7 @@ impl<'a> CertificateBuilder<'a> { pub fn add_ips( &'a mut self, ips: Vec<&'a str>, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { match &mut self.ips { None => { self.ips = Some(ips); @@ -204,7 +204,7 @@ impl<'a> CertificateBuilder<'a> { pub fn add_extensions( &'a mut self, extensions: Vec, - ) -> &mut CertificateBuilder<'a> { + ) -> &'a mut CertificateBuilder<'a> { match &mut self.extensions { None => { self.extensions = Some(extensions); diff --git a/keylime/src/device_id.rs b/keylime/src/device_id.rs new file mode 100644 index 000000000..38a61070f --- /dev/null +++ b/keylime/src/device_id.rs @@ -0,0 +1,678 @@ +use crate::{ + crypto::{self, CryptoError}, + tpm::{self, IAKResult, IDevIDResult, TpmError}, +}; +use log::*; +use openssl::x509::X509; +use std::path::Path; +use thiserror::Error; +use tss_esapi::{ + handles::KeyHandle, + interface_types::algorithm::{AsymmetricAlgorithm, HashingAlgorithm}, + structures::{Attest, Data, Public as TssPublic, Signature}, +}; + +#[derive(Error, Debug)] +pub enum DeviceIDBuilderError { + /// Failed to load certificate + #[error("Failed to load certificate")] + CertLoad(#[source] crypto::CryptoError), + + /// Public key does not match the certificate + #[error("Public key does not match the certificate")] + CertPubKeyMismatch(#[source] tpm::TpmError), + + /// Failed to flush context + #[error("Failed to flush context")] + FlushContext(#[source] TpmError), + + /// Failed to create IAK + #[error("Failed to create IAK")] + IAKCreate(#[source] TpmError), + + /// IAK default Template not set + #[error("IAK default template not set")] + IAKDefaultTemplateNotSet, + + /// Could not get IAK from provided handle + #[error("Could not get IAK from provided handle")] + IAKFromHandle(#[source] TpmError), + + /// IAK handle not set in DeviceIDBuilder + #[error("IAK handle not set in DeviceBuilder. Set the IAK handle with the iak_handle() method from the DeviceIDBuilder object")] + IAKHandleNotSet, + + /// IAK password not set in DeviceIDBuilder + #[error("IAK password not set in DeviceBuilder. Set the IAK password with the iak_password() method from the DeviceIDBuilder object")] + IAKPasswordNotSet, + + /// Failed to create IDevID + #[error("Failed to create IDevID")] + IDevIDCreate(#[source] TpmError), + + /// IDevID default Template not set + #[error("IDevID default template not set")] + IDevIDDefaultTemplateNotSet, + + /// Could not get IDevID from provided handle + #[error("Could not get IDevID from provided handle")] + IDevIDFromHandle(#[source] TpmError), + + /// IDevID handle not set in DeviceIDBuilder + #[error("IDevID handle not set in DeviceBuilder. Set the IDevID handle with the idevid_handle() method from the DeviceIDBuilder object")] + IDevIDHandleNotSet, + + /// IDevID password not set in DeviceIDBuilder + #[error("IDevID password not set in DeviceBuilder. Set the IDevID password with the idevid_password() method from the DeviceIDBuilder object")] + IDevIDPasswordNotSet, + + /// Failed to get template from certificate + #[error("Failed to get template from certificate")] + TemplateFromCert(#[source] CryptoError), + + /// Failed to obtain template + #[error("Failed to obtain template")] + Template(#[source] TpmError), +} + +#[derive(Error, Debug)] +pub enum DeviceIDError { + /// Failed to certify + #[error("Failed to certify credential with IAK")] + Certify(#[source] TpmError), +} + +#[derive(Debug, Default)] +pub struct DeviceIDBuilder<'a> { + iak_asym_alg: Option<&'a str>, + iak_cert: Option, + iak_cert_path: Option<&'a str>, + iak_default_template: Option<&'a str>, + iak_handle: Option<&'a str>, + iak_hash_alg: Option<&'a str>, + iak_password: Option<&'a str>, + iak_template: Option<&'a str>, + idevid_asym_alg: Option<&'a str>, + idevid_cert: Option, + idevid_cert_path: Option<&'a str>, + idevid_default_template: Option<&'a str>, + idevid_handle: Option<&'a str>, + idevid_hash_alg: Option<&'a str>, + idevid_password: Option<&'a str>, + idevid_template: Option<&'a str>, +} + +impl<'a> DeviceIDBuilder<'a> { + /// Create a new DeviceIDBuilder object + pub fn new() -> DeviceIDBuilder<'a> { + Self::default() + } + + /// Set the IAK handle to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * iak_handle (&str): The IAK handle + pub fn iak_handle(mut self, iak_handle: &'a str) -> DeviceIDBuilder<'a> { + self.iak_handle = Some(iak_handle); + self + } + + /// Set the IAK password to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * iak_password (&str): The IAK password + pub fn iak_password( + mut self, + iak_password: &'a str, + ) -> DeviceIDBuilder<'a> { + self.iak_password = Some(iak_password); + self + } + + /// Set the path to the IAK certificate to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * path (&str): The path to the IAK certificate + pub fn iak_cert_path(mut self, path: &'a str) -> DeviceIDBuilder<'a> { + self.iak_cert_path = Some(path); + self + } + + /// Set the default template to use for the IAK + /// + /// When `iak_cert` is not set, the provided template is used when `iak_template` string is + /// empty, or the special keywords 'detect' or 'default' are set + /// + /// # Arguments: + /// + /// * template(&str): The name of the default template to use. + pub fn iak_default_template( + mut self, + template: &'a str, + ) -> DeviceIDBuilder<'a> { + self.iak_default_template = Some(template); + self + } + + /// Set the template to use for the IAK + /// + /// If the string is empty, or the special keywords 'detect' or 'default' are set, the template + /// will be obtained from the IAK certificate provided in `iak_cert` + /// + /// # Arguments: + /// + /// * template(&str): The template name to use. + pub fn iak_template(mut self, template: &'a str) -> DeviceIDBuilder<'a> { + self.iak_template = Some(template); + self + } + + /// Set the Asymmetric algorithm to use in the IAK template + /// + /// # Arguments: + /// + /// * iak_asym_alg (&str): The template Asymmetric algorithm to use for the IAK template + pub fn iak_asym_alg(mut self, asym_alg: &'a str) -> DeviceIDBuilder<'a> { + self.iak_asym_alg = Some(asym_alg); + self + } + + /// Set the template Hash algorithm to use in the IAK template + /// + /// # Arguments: + /// + /// * iak_hash_alg (&str): The Hash algorithm to use for the IAK template + pub fn iak_hash_alg(mut self, hash_alg: &'a str) -> DeviceIDBuilder<'a> { + self.iak_hash_alg = Some(hash_alg); + self + } + + /// Set the IDevID handle to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * idevid_handle (&str): The IDevID handle + pub fn idevid_handle( + mut self, + idevid_handle: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_handle = Some(idevid_handle); + self + } + + /// Set the IDevID password to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * idevid_password (&str): The password to use for the IDevID + pub fn idevid_password( + mut self, + idevid_password: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_password = Some(idevid_password); + self + } + + /// Set the IDevID certificate path to use when building the DeviceID object + /// + /// # Arguments: + /// + /// * path (&str): The path to the IDevID certificate + pub fn idevid_cert_path(mut self, path: &'a str) -> DeviceIDBuilder<'a> { + self.idevid_cert_path = Some(path); + self + } + + /// Set the default template to use for the IDevID + /// + /// When `idevid_cert` is not set, the provided template is used when `idevid_template` string is + /// empty, or the special keywords 'detect' or 'default' are set + /// + /// # Arguments: + /// + /// * template(&str): The name of the default template to use. + pub fn idevid_default_template( + mut self, + template: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_default_template = Some(template); + self + } + + /// Set the template to use for the IDevID + /// + /// If the string is empty, or the special keywords 'detect' or 'default' are set, the template + /// will be obtained from the IDevID certificate provided in `idevid_cert` + /// + /// # Arguments: + /// + /// * template(&str): The template name to use. + pub fn idevid_template( + mut self, + template: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_template = Some(template); + self + } + + /// Set the Asymmetric algorithm to use in the IDevID template + /// + /// # Arguments: + /// + /// * idevid_asym_alg (&str): The template Asymmetric algorithm to use for the IDevID template + pub fn idevid_asym_alg( + mut self, + asym_alg: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_asym_alg = Some(asym_alg); + self + } + + /// Set the template Hash algorithm to use in the IDevID template + /// + /// # Arguments: + /// + /// * idevid_hash_alg (&str): The Hash algorithm to use for the IDevID template + pub fn idevid_hash_alg( + mut self, + hash_alg: &'a str, + ) -> DeviceIDBuilder<'a> { + self.idevid_hash_alg = Some(hash_alg); + self + } + + /// Get the IAK template + /// + /// If configured to detect, get the template from the IAK certificate + /// Otherwise, construct the template from the provided algorithms + fn get_iak_template( + &mut self, + ) -> Result<(AsymmetricAlgorithm, HashingAlgorithm), DeviceIDBuilderError> + { + let iak_cert = self.get_iak_cert()?; + + // If the IAK cert is set, get the template based on it + // Otherwise use the default template set + let detected = match iak_cert { + Some(cert) => crypto::match_cert_to_template(cert) + .map_err(DeviceIDBuilderError::TemplateFromCert)?, + None => { + if let Some(default) = self.iak_default_template { + default.trim().to_string() + } else { + return Err( + DeviceIDBuilderError::IAKDefaultTemplateNotSet, + ); + } + } + }; + + let template = self.iak_template.unwrap_or("").trim(); + let asym_alg = self.iak_asym_alg.unwrap_or("").trim(); + let hash_alg = self.iak_hash_alg.unwrap_or("").trim(); + + tpm::get_idevid_template(&detected, template, asym_alg, hash_alg) + .map_err(DeviceIDBuilderError::Template) + } + + /// Get the IDevID template + /// + /// If configured to detect, get the template from the IDevID certificate + /// Otherwise, construct the template from the provided algorithms + fn get_idevid_template( + &mut self, + ) -> Result<(AsymmetricAlgorithm, HashingAlgorithm), DeviceIDBuilderError> + { + let idevid_cert = self.get_idevid_cert()?; + + // If the IAK cert is set, get the template based on it + // Otherwise use the default template set + let detected = match idevid_cert { + Some(cert) => crypto::match_cert_to_template(cert) + .map_err(DeviceIDBuilderError::TemplateFromCert)?, + None => { + if let Some(default) = self.idevid_default_template { + default.trim().to_string() + } else { + return Err( + DeviceIDBuilderError::IDevIDDefaultTemplateNotSet, + ); + } + } + }; + + let template = self.idevid_template.unwrap_or("").trim(); + let asym_alg = self.idevid_asym_alg.unwrap_or("").trim(); + let hash_alg = self.idevid_hash_alg.unwrap_or("").trim(); + + tpm::get_idevid_template(&detected, template, asym_alg, hash_alg) + .map_err(DeviceIDBuilderError::Template) + } + + /// Get the IAK from the given handle and password or recreate following the given template + /// + /// If a handle has been set, try to obtain the IAK from the handle + /// If there is a configured IAK password, add the password to the handle + /// + /// If the handle is empty, recreate the IAK using the provided algorithms + fn get_iak( + &mut self, + tpm_ctx: &mut tpm::Context, + ) -> Result { + let (asym_alg, hash_alg) = self.get_iak_template()?; + match self.iak_handle { + Some(handle) => { + if handle.trim().is_empty() { + info!("Recreating IAK."); + tpm_ctx + .create_iak(asym_alg, hash_alg) + .map_err(DeviceIDBuilderError::IAKCreate) + } else { + let password = self.iak_password.unwrap_or("").trim(); + info!("Collecting persisted IAK."); + tpm_ctx + .iak_from_handle(handle.trim(), password) + .map_err(DeviceIDBuilderError::IAKFromHandle) + } + } + None => Err(DeviceIDBuilderError::IAKHandleNotSet), + } + } + + /// Get the IDevID from the given handle and password or recreate following the given template + /// + /// If a handle has been set, try to obtain the IDevID from the handle + /// If there is a configured IDevID password, add the password to the handle + /// + /// If the handle is empty, recreate the IDevId using the provided algorithms + fn get_idevid( + &mut self, + tpm_ctx: &mut tpm::Context, + ) -> Result { + let (asym_alg, hash_alg) = self.get_idevid_template()?; + match self.idevid_handle { + Some(handle) => { + if handle.trim().is_empty() { + info!("Recreating IDevID."); + tpm_ctx + .create_idevid(asym_alg, hash_alg) + .map_err(DeviceIDBuilderError::IDevIDCreate) + } else { + let password = self.idevid_password.unwrap_or("").trim(); + info!("Collecting persisted IDevID."); + tpm_ctx + .idevid_from_handle(handle.trim(), password.trim()) + .map_err(DeviceIDBuilderError::IDevIDFromHandle) + } + } + None => Err(DeviceIDBuilderError::IDevIDHandleNotSet), + } + } + + /// Get the IAK certificate + /// If the iak_cert is not set, try to load the certificate from the iak_cert_path and cache + /// the loaded certificate + fn get_iak_cert( + &mut self, + ) -> Result, DeviceIDBuilderError> { + match self.iak_cert { + Some(ref cert) => Ok(Some(cert)), + None => match &self.iak_cert_path { + Some(path) => { + if path.trim().is_empty() { + debug!( + "The IAK certificate was not set in the configuration file and will be ignored" + ); + Ok(None) + } else { + self.iak_cert = Some(crypto::load_x509(Path::new(path.trim())).map_err(|e| { + debug!("Could not load IAK certificate from {path}: {e}"); + e + }).map_err(DeviceIDBuilderError::CertLoad)?); + if let Some(ref cert) = &self.iak_cert { + Ok(Some(cert)) + } else { + unreachable!(); + } + } + } + None => Ok(None), + }, + } + } + + /// Get the IDevID certificate + /// + /// If the idevid_cert is not set, try to load the certificate from the idevid_cert_path and cache the loaded certificate + fn get_idevid_cert( + &mut self, + ) -> Result, DeviceIDBuilderError> { + match self.idevid_cert { + Some(ref cert) => Ok(Some(cert)), + None => match self.idevid_cert_path { + Some(path) => { + if path.trim().is_empty() { + debug!( + "The IDevId certificate was not set in the configuration file and will be ignored" + ); + Ok(None) + } else { + self.idevid_cert = Some(crypto::load_x509(Path::new(path.trim())).map_err(|e| { + debug!("Could not load IAK certificate from {path}: {e}"); + e + }).map_err(DeviceIDBuilderError::CertLoad)?); + if let Some(ref cert) = self.idevid_cert { + Ok(Some(cert)) + } else { + unreachable!(); + } + } + } + None => Ok(None), + }, + } + } + + /// Generate the DeviceID object using the previously set options + pub fn build( + mut self, + tpm_ctx: &mut tpm::Context, + ) -> Result { + let idevid = self.get_idevid(tpm_ctx)?; + + // Flush the IDevID handle to free TPM memory + if !idevid.is_persistent { + tpm_ctx + .flush_context(idevid.handle.into()) + .map_err(DeviceIDBuilderError::FlushContext)?; + } + + // Check that recreated/collected IDevID key matches the one in the certificate + let idevid_cert = match self.idevid_cert.take() { + Some(cert) => { + tpm::check_pubkey_match_cert(&idevid.public, &cert, "IDevID") + .map_err(DeviceIDBuilderError::CertPubKeyMismatch)?; + + Some(cert) + } + None => None, + }; + + let iak = self.get_iak(tpm_ctx)?; + let iak_cert = match self.iak_cert.take() { + Some(cert) => { + tpm::check_pubkey_match_cert(&iak.public, &cert, "IDevID") + .map_err(DeviceIDBuilderError::CertPubKeyMismatch)?; + + Some(cert) + } + None => None, + }; + + Ok(DeviceID { + iak_handle: iak.handle, + iak_handle_persistent: iak.is_persistent, + iak_pubkey: iak.public, + iak_cert, + idevid_pubkey: idevid.public, + idevid_cert, + }) + } +} + +#[derive(Debug)] +pub struct DeviceID { + pub iak_handle: KeyHandle, + pub iak_handle_persistent: bool, + pub iak_pubkey: TssPublic, + pub iak_cert: Option, + pub idevid_pubkey: TssPublic, + pub idevid_cert: Option, +} + +impl DeviceID { + /// Certify IAK against AK using the provided qualifying data + /// + /// # Arguments: + /// + /// * qualifying_data (Data): The qualifying data. + /// * ak (KeyHandle): The AK handle + pub fn certify( + &mut self, + qualifying_data: Data, + ak: KeyHandle, + tpm_ctx: &mut tpm::Context, + ) -> Result<(Attest, Signature), DeviceIDError> { + tpm_ctx + .certify_credential_with_iak(qualifying_data, ak, self.iak_handle) + .map_err(DeviceIDError::Certify) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_device_id_builder_setting() { + let _builder = DeviceIDBuilder::new() + .iak_handle("") + .iak_cert_path("") + .iak_password("") + .iak_default_template("") + .iak_template("") + .iak_asym_alg("") + .iak_hash_alg("") + .idevid_handle("") + .idevid_cert_path("") + .idevid_password("") + .idevid_default_template("") + .idevid_template("") + .idevid_asym_alg("") + .idevid_hash_alg(""); + } + + #[tokio::test] + #[cfg(feature = "testing")] + async fn test_device_id_builder() { + let _mutex = tpm::testing::lock_tests().await; + let certs_dir = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("test-data") + .join("iak-idevid-certs"); + + if certs_dir.exists() { + let iak_cert = certs_dir.join("iak.cert.pem"); + let idevid_cert = certs_dir.join("idevid.cert.pem"); + if iak_cert.exists() && idevid_cert.exists() { + let mut tpm_ctx = tpm::Context::new().unwrap(); //#[allow_ci] + let result = DeviceIDBuilder::new() + .iak_handle("") + .iak_cert_path( + iak_cert + .to_str() + .expect("Failed to get str for IAK cert"), + ) + .iak_password("") + .iak_default_template("") + .iak_template("") + .iak_asym_alg("") + .iak_hash_alg("") + .idevid_handle("") + .idevid_cert_path( + idevid_cert + .to_str() + .expect("Failed to get str for IDevID cert"), + ) + .idevid_password("") + .idevid_default_template("") + .idevid_template("") + .idevid_asym_alg("") + .idevid_hash_alg("") + .build(&mut tpm_ctx); + assert!(result.is_ok(), "Result: {result:?}"); + let dev_id = result.unwrap(); //#[allow_ci] + + // Flush context to free TPM memory + let r = tpm_ctx.flush_context(dev_id.iak_handle.into()); + assert!(r.is_ok(), "Result: {r:?}"); + } + } + } + + #[tokio::test] + #[cfg(feature = "testing")] + async fn test_device_id_builder_no_certs() { + let _mutex = tpm::testing::lock_tests().await; + let mut tpm_ctx = tpm::Context::new().unwrap(); //#[allow_ci] + let result = DeviceIDBuilder::new() + .iak_handle("") + .iak_password("") + .iak_default_template("H-1") + .iak_template("detect") + .iak_asym_alg("") + .iak_hash_alg("") + .idevid_handle("") + .idevid_password("") + .idevid_default_template("H-1") + .idevid_template("detect") + .idevid_asym_alg("") + .idevid_hash_alg("") + .build(&mut tpm_ctx); + assert!(result.is_ok(), "Result: {result:?}"); + let dev_id = result.unwrap(); //#[allow_ci] + + // Flush context to free TPM memory + let r = tpm_ctx.flush_context(dev_id.iak_handle.into()); + assert!(r.is_ok(), "Result: {r:?}"); + } + + #[tokio::test] + #[cfg(feature = "testing")] + async fn test_device_id_builder_no_certs_no_default() { + let _mutex = tpm::testing::lock_tests().await; + let mut tpm_ctx = tpm::Context::new().unwrap(); //#[allow_ci] + let result = DeviceIDBuilder::new() + .iak_handle("") + .iak_password("") + .iak_default_template("") + .iak_template("detect") + .iak_asym_alg("rsa") + .iak_hash_alg("sha256") + .idevid_handle("") + .idevid_password("") + .idevid_default_template("") + .idevid_template("detect") + .idevid_asym_alg("rsa") + .idevid_hash_alg("sha256") + .build(&mut tpm_ctx); + assert!(result.is_ok(), "Result: {result:?}"); + let dev_id = result.unwrap(); //#[allow_ci] + + // Flush context to free TPM memory + let r = tpm_ctx.flush_context(dev_id.iak_handle.into()); + assert!(r.is_ok(), "Result: {r:?}"); + } +} diff --git a/keylime/src/hostname_parser.rs b/keylime/src/hostname_parser.rs index dbf971a65..650e8277d 100644 --- a/keylime/src/hostname_parser.rs +++ b/keylime/src/hostname_parser.rs @@ -60,7 +60,7 @@ pub fn parse_hostname(hostname: &str) -> Result<&str, HostnameParsingError> { else { return Err(HostnameParsingError::InvalidInput(hostname.to_string())); }; - return Ok(pair.as_str()); + Ok(pair.as_str()) } // Unit Testing diff --git a/keylime/src/ip_parser.rs b/keylime/src/ip_parser.rs index 94b3aa7d2..31c6c180b 100644 --- a/keylime/src/ip_parser.rs +++ b/keylime/src/ip_parser.rs @@ -85,7 +85,7 @@ pub fn parse_ip(ip: &str) -> Result<&str, IpParsingError> { else { return Err(IpParsingError::InvalidInput(ip.to_string())); }; - return get_inner_ip(pair); + get_inner_ip(pair) } // Unit Testing diff --git a/keylime/src/lib.rs b/keylime/src/lib.rs index 791729e13..1d10e02bc 100644 --- a/keylime/src/lib.rs +++ b/keylime/src/lib.rs @@ -1,5 +1,6 @@ pub mod algorithms; pub mod crypto; +pub mod device_id; pub mod hostname_parser; pub mod ima; pub mod ip_parser; diff --git a/keylime/src/tpm.rs b/keylime/src/tpm.rs index dafddf022..997334dc4 100644 --- a/keylime/src/tpm.rs +++ b/keylime/src/tpm.rs @@ -1,8 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2021 Keylime Authors -use crate::algorithms::{ - AlgorithmError, EncryptionAlgorithm, HashAlgorithm, SignAlgorithm, +use crate::{ + algorithms::{ + AlgorithmError, EncryptionAlgorithm, HashAlgorithm, SignAlgorithm, + }, + crypto, }; use base64::{engine::general_purpose, Engine as _}; use log::*; @@ -18,6 +21,7 @@ use openssl::{ hash::{Hasher, MessageDigest}, memcmp, pkey::{HasPublic, Id, PKeyRef, Public}, + x509::X509, }; use tss_esapi::{ @@ -50,10 +54,11 @@ use tss_esapi::{ Attest, AttestInfo, Auth, Data, Digest, DigestValues, EccParameter, EccPoint, EccScheme, EncryptedSecret, HashScheme, IdObject, KeyDerivationFunctionScheme, Name, PcrSelectionList, - PcrSelectionListBuilder, PcrSlot, PublicBuilder, - PublicEccParametersBuilder, PublicKeyRsa, PublicRsaParametersBuilder, - RsaExponent, RsaScheme, Signature, SignatureScheme, - SymmetricDefinitionObject, Ticket, VerifiedTicket, + PcrSelectionListBuilder, PcrSlot, Private as TssPrivate, + Public as TssPublic, PublicBuilder, PublicEccParametersBuilder, + PublicKeyRsa, PublicRsaParametersBuilder, RsaExponent, RsaScheme, + Signature, SignatureScheme, SymmetricDefinitionObject, Ticket, + VerifiedTicket, }, tcti_ldr::TctiNameConf, traits::Marshall, @@ -114,6 +119,10 @@ const UNIQUE_IAK: [u8; 3] = [0x49, 0x41, 0x4b]; /// TpmError wraps all possible errors raised in tpm.rs #[derive(Error, Debug)] pub enum TpmError { + /// Public key does not match with certificate + #[error("{0} key does not match with certificate. Check template in configuration.")] + PublicKeyCertificateMismatch(String), + /// Unsupported hashing algorithm error #[error("Unsupported hashing algorithm : {alg:?}")] UnsupportedHashingAlgorithm { alg: HashingAlgorithm }, @@ -405,6 +414,10 @@ pub enum TpmError { #[error("AlgorithmError")] AlgorithmError(#[from] AlgorithmError), + /// Generic catch-all crypto error + #[error("CryptoError")] + CryptoError(#[from] crypto::CryptoError), + /// Generic catch-all error #[error("{0}")] Other(String), @@ -430,40 +443,42 @@ type Result = std::result::Result; pub struct EKResult { pub key_handle: KeyHandle, pub ek_cert: Option>, - pub public: tss_esapi::structures::Public, + pub public: TssPublic, } /// Holds the output of create_ak. #[derive(Clone, Debug)] pub struct AKResult { - pub public: tss_esapi::structures::Public, - pub private: tss_esapi::structures::Private, + pub public: TssPublic, + pub private: TssPrivate, } /// Holds the output of create_iak. #[derive(Clone, Debug)] pub struct IAKResult { - pub public: tss_esapi::structures::Public, + pub public: TssPublic, pub handle: tss_esapi::handles::KeyHandle, + pub is_persistent: bool, } /// Holds the output of create_idevid. #[derive(Clone, Debug)] pub struct IDevIDResult { - pub public: tss_esapi::structures::Public, + pub public: TssPublic, pub handle: tss_esapi::handles::KeyHandle, + pub is_persistent: bool, } /// Holds the Public result from create_idevid_public_from_default_template #[derive(Clone, Debug)] pub struct IDevIDPublic { - pub public: tss_esapi::structures::Public, + pub public: TssPublic, } /// Holds the Public result from create_iak_public_from_default_template #[derive(Clone, Debug)] pub struct IAKPublic { - pub public: tss_esapi::structures::Public, + pub public: TssPublic, } /// Wrapper around tss_esapi::Context. @@ -474,7 +489,7 @@ pub struct Context<'a> { static TPM_CTX: OnceLock>> = OnceLock::new(); -impl<'a> Context<'a> { +impl Context<'_> { /// Creates a connection context. pub fn new() -> Result { let tcti_path = match std::env::var("TCTI") { @@ -735,6 +750,7 @@ impl<'a> Context<'a> { Ok(IDevIDResult { public: idevid_pub, handle: idevid_handle, + is_persistent: true, }) } @@ -754,6 +770,7 @@ impl<'a> Context<'a> { Ok(IAKResult { public: iak_pub, handle: iak_handle, + is_persistent: true, }) } @@ -805,6 +822,7 @@ impl<'a> Context<'a> { Ok(IDevIDResult { public: primary_key.out_public, handle: primary_key.key_handle, + is_persistent: false, }) } @@ -1022,6 +1040,7 @@ impl<'a> Context<'a> { Ok(IAKResult { public: primary_key.out_public, handle: primary_key.key_handle, + is_persistent: false, }) } @@ -1911,6 +1930,23 @@ pub fn get_idevid_template( Ok((asym_alg, name_alg)) } +/// Check if a public key and certificate match +/// +/// The provided label is used to generate logging messages +pub fn check_pubkey_match_cert( + pubkey: &TssPublic, + certificate: &X509, + label: &str, +) -> Result<()> { + if crypto::check_x509_key(certificate, pubkey)? { + info!("{label} public key matches certificate."); + Ok(()) + } else { + error!("{label} public key does not match certificate. Check template in configuration."); + Err(TpmError::PublicKeyCertificateMismatch(label.to_string())) + } +} + pub mod testing { use super::*; #[cfg(feature = "testing")] diff --git a/tests/generate-iak-idevid-certs.sh b/tests/generate-iak-idevid-certs.sh index 894bf41d2..a619c95ff 100755 --- a/tests/generate-iak-idevid-certs.sh +++ b/tests/generate-iak-idevid-certs.sh @@ -159,7 +159,7 @@ pushd "${OUTPUTDIR}" > /dev/null || exit 1 -out cacert.pem popd > /dev/null || exit 1 cat intermediate/cacert.pem root/cacert.pem \ - > cert-chain.pem + > ca-cert-chain.pem popd > /dev/null || exit 1 mkdir "${OUTPUTDIR}/ikeys" diff --git a/tests/run.sh b/tests/run.sh index 3a0c6b5d9..09714d80d 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -2,6 +2,19 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2021 Keylime Authors +# Check that the script is running from inside the repository tree +GIT_ROOT=$(git rev-parse --show-toplevel) || { + echo "Please run this script from inside the rust-keylime repository tree" + exit 1 +} + +TESTS_DIR="${GIT_ROOT}/tests" +TEST_DATA_DIR="${GIT_ROOT}/test-data" +TPMDIR="${TEST_DATA_DIR}/tpm-state" + +# These certificates are used for the keylime/device_id tests +IAK_IDEVID_CERTS="${GIT_ROOT}/keylime/test-data/iak-idevid-certs" + # Store the old TCTI setting OLD_TCTI=$TCTI OLD_TPM2TOOLS_TCTI=$TPM2TOOLS_TCTI @@ -11,14 +24,13 @@ set -euf -o pipefail echo "-------- Setting up Software TPM" -# Create temporary directories -TEMPDIR=$(mktemp -d) -TPMDIR="${TEMPDIR}/tpmdir" -mkdir -p ${TPMDIR} +if [[ ! -d "${TPMDIR}" ]]; then + mkdir -p "${TPMDIR}" +fi # Manufacture a new Software TPM swtpm_setup --tpm2 \ - --tpmstate ${TPMDIR} \ + --tpmstate "${TPMDIR}" \ --createek --decryption --create-ek-cert \ --create-platform-cert \ --lock-nvram \ @@ -29,7 +41,7 @@ swtpm_setup --tpm2 \ function start_swtpm { # Initialize the swtpm socket swtpm socket --tpm2 \ - --tpmstate dir=${TPMDIR} \ + --tpmstate dir="${TPMDIR}" \ --flags startup-clear \ --ctrl type=tcp,port=2322 \ --server type=tcp,port=2321 \ @@ -39,7 +51,7 @@ function start_swtpm { function stop_swtpm { # Stop swtpm if running - if [[ -n "$SWTPM_PID" ]]; then + if [[ -n "${SWTPM_PID}" ]]; then echo "Stopping swtpm" kill $SWTPM_PID fi @@ -72,6 +84,25 @@ RUST_BACKTRACE=1 cargo build echo "-------- Testing" start_swtpm + + +# Check that tpm2-openssl provider is available +if openssl list -provider tpm2 -providers > /dev/null; then + # If any IAK/IDevID related certificate is missing, re-generate them + if [[ ( ! -f "${IAK_IDEVID_CERTS}/iak.cert.pem" ) || + ( ! -f "${IAK_IDEVID_CERTS}/iak.cert.der" ) || + ( ! -f "${IAK_IDEVID_CERTS}/idevid.cert.pem" ) || + ( ! -f "${IAK_IDEVID_CERTS}/idevid.cert.der" ) || + ( ! -f "${IAK_IDEVID_CERTS}/ca-cert-chain.pem" ) ]] + then + # Remove any leftover from old certificates + rm -rf "${IAK_IDEVID_CERTS}" + mkdir -p "${IAK_IDEVID_CERTS}" + echo "-------- Create IAK/IDevID certificates" + "${GIT_ROOT}/tests/generate-iak-idevid-certs.sh" -o "${IAK_IDEVID_CERTS}" + fi +fi + mkdir -p /var/lib/keylime RUST_BACKTRACE=1 RUST_LOG=info \ KEYLIME_CONFIG=$PWD/keylime-agent.conf \