From 1c20f8c7bcd7d065c0a0f661cfb8984c4717360d Mon Sep 17 00:00:00 2001 From: Isaac Matthews Date: Mon, 4 Dec 2023 10:22:13 +0000 Subject: [PATCH] detect idevid template from certificates Signed-off-by: Isaac Matthews --- keylime-agent.conf | 10 ++-- keylime-agent/src/crypto.rs | 30 ++++++++++++ keylime-agent/src/main.rs | 97 +++++++++++++++++++------------------ keylime/src/tpm.rs | 11 ++++- 4 files changed, 96 insertions(+), 52 deletions(-) diff --git a/keylime-agent.conf b/keylime-agent.conf index 012b3e0dc..c71c1845a 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -228,19 +228,21 @@ tpm_signing_alg = "rsassa" ek_handle = "generate" # Enable IDevID and IAK usage and set their algorithms. -# Choosing a template will override the name and asymmetric algorithm choices. +# By default the template will be detected automatically from the certificates. This will happen if iak_idevid_template is left empty or set as "default" or "detect". +# Choosing a template will override the name and asymmetric algorithm choices. To use these choices, set iak_idevid_template to "manual" # Templates are specified in the TCG document found here, section 7.3.4: # https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf # # Accepted values: +# iak_idevid_template: default, detect, H-1, H-2, H-3, H-4, H-5, manual # iak_idevid_asymmetric_alg: rsa, ecc # iak_idevid_name_alg: sha256, sm3_256, sha384, sha512 -# iak_idevid_template: H-1, H-2, H-3, H-4, H-5 -# Leave template as "" in order to use asymmetric and name algorithm options enable_iak_idevid = false +iak_idevid_template = "detect" +# In order for these values to be used, set the iak_idevid_template option to manual iak_idevid_asymmetric_alg = "rsa" iak_idevid_name_alg = "sha256" -iak_idevid_template = "H-1" + # The name of the file containing the X509 IAK certificate. # If set as "default", the "iak-cert.crt" value is used diff --git a/keylime-agent/src/crypto.rs b/keylime-agent/src/crypto.rs index 3b71a7aa4..f7f7db378 100644 --- a/keylime-agent/src/crypto.rs +++ b/keylime-agent/src/crypto.rs @@ -137,6 +137,36 @@ pub(crate) fn check_x509_key( } } +/// Detect a template from a certificate +/// Templates defined in: TPM 2.0 Keys for Device Identity and Attestation at https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf +pub(crate) fn match_cert_to_template(cert: &X509) -> 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 + let id_rsa_pss: Id = Id::from_raw(912); + match cert.public_key()?.id() { + Id::RSA => match cert.public_key()?.bits() { + 2048 => Ok("H-1".to_string()), + _ => Ok("".to_string()), + }, + cert_id if cert_id == id_rsa_pss => match cert.public_key()?.bits() { + 2048 => Ok("H-1".to_string()), + _ => Ok("".to_string()), + }, + Id::EC => match cert.public_key()?.bits() { + 256 => match cert.public_key()?.ec_key()?.group().curve_name() { + Some(Nid::SECP256K1) => Ok("H-2".to_string()), + _ => Ok("H-5".to_string()), + }, + 384 => Ok("H-3".to_string()), + 521 => Ok("H-4".to_string()), + _ => Ok("".to_string()), + }, + _ => Err(Error::Other( + "Certificate does not seem to have an RSA or EC key".to_string(), + )), + } +} + /// Read a PEM file and returns the public and private keys pub(crate) fn load_key_pair( key_path: &Path, diff --git a/keylime-agent/src/main.rs b/keylime-agent/src/main.rs index 574014241..2f0461cdb 100644 --- a/keylime-agent/src/main.rs +++ b/keylime-agent/src/main.rs @@ -277,29 +277,9 @@ async fn main() -> Result<()> { config.agent.tpm_signing_alg.as_ref(), )?; - let (asym_alg, name_alg) = tpm::get_idevid_template( - config.agent.iak_idevid_template.as_str(), - config.agent.iak_idevid_asymmetric_alg.as_str(), - config.agent.iak_idevid_name_alg.as_str(), - )?; - - let (iak, idevid) = if config.agent.enable_iak_idevid { - let idevid = ctx.create_idevid(asym_alg, name_alg)?; - info!("IDevID created."); - // Flush after creating to make room for AK and EK and IAK - ctx.as_mut().flush_context(idevid.handle.into())?; - let iak = ctx.create_iak(asym_alg, name_alg)?; - info!("IAK created."); - (Some(iak), Some(idevid)) - } else { - (None, None) - }; - let iak_cert: Option; let idevid_cert: Option; // Attempt to load the IAK and IDevID certificates - // Check the certificates contain the keys that have been regenerated by the TPM - // If they do not there is likely a configuration issue where the template has been set to the wrong value if config.agent.enable_iak_idevid { iak_cert = match config.agent.iak_cert.as_ref() { "" => { @@ -317,19 +297,7 @@ async fn main() -> Result<()> { Ok(cert) => cert, Err(error) => crypto::load_x509_pem(iak_path)?, }; - if crypto::check_x509_key( - &iakcert, - iak.clone() - .expect( - "IAK could not be used in cert key check.", - ) - .public, - )? { - Some(iakcert) - } else { - error!("IAK template does not match certificate. Check template in configuration."); - return Err(Error::Configuration("IAK template does not match certificate. Check template in configuration.".to_string())); - } + Some(iakcert) } else { debug!("Can not find IAK certificate"); None @@ -352,20 +320,7 @@ async fn main() -> Result<()> { Ok(cert) => cert, Err(error) => crypto::load_x509_pem(idevid_path)?, }; - if crypto::check_x509_key( - &idevcert, - idevid - .clone() - .expect( - "IDevID could not be used in cert key check.", - ) - .public, - )? { - Some(idevcert) - } else { - error!("IDevID template does not match certificate. Check template in configuration."); - return Err(Error::Configuration("IDevID template does not match certificate. Check template in configuration.".to_string())); - } + Some(idevcert) } else { debug!("Can not find IDevID certificate"); None @@ -376,6 +331,54 @@ async fn main() -> Result<()> { iak_cert = None; idevid_cert = None; } + /// Regenerate the IAK and IDevID and check that the keys match the certificates that have been loaded + let (iak, idevid) = if config.agent.enable_iak_idevid { + 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(), + )?; + + let idevid = ctx.create_idevid(asym_alg, name_alg)?; + info!("IDevID created."); + // Flush after creating to make room for AK and EK and IAK + ctx.as_mut().flush_context(idevid.handle.into())?; + if crypto::check_x509_key( + &idevid_cert.clone().ok_or(Error::Other( + "IAK/IDevID enabled but cert could not be used".to_string(), + ))?, + idevid.clone().public, + )? { + info!("Regenerated IDevID matches certificate."); + } else { + error!("IDevID template does not match certificate. Check template in configuration."); + return Err(Error::Configuration("IDevID template does not match certificate. Check template in configuration.".to_string())); + } + + let iak = ctx.create_iak(asym_alg, name_alg)?; + info!("IAK created."); + if crypto::check_x509_key( + &iak_cert.clone().ok_or(Error::Other( + "IAK/IDevID enabled but cert could not be used".to_string(), + ))?, + iak.clone().public, + )? { + info!("Regenerated IAK matches certificate."); + } else { + error!("IAK template does not match certificate. Check template in configuration."); + return Err(Error::Configuration("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() { diff --git a/keylime/src/tpm.rs b/keylime/src/tpm.rs index 4b90ca627..47ac04c14 100644 --- a/keylime/src/tpm.rs +++ b/keylime/src/tpm.rs @@ -105,8 +105,12 @@ const IAK_AUTH_POLICY_SHA256: [u8; 32] = [ ]; const UNIQUE_IAK: [u8; 3] = [0x49, 0x41, 0x4b]; -/// Return the asymmetric and name algorithms, either by matching to a template or using the user specified algorithms if no template is set +/// Return the asymmetric and name algorithms, either by matching to a template or using the user specified algorithms +/// If the template config option is set to "", "detect" or "default", the template will be matched to the certs +/// If a template has been specified, that will be used +/// If the option has been set to "manual", or some other not-empty string, the user specified algorithms will be used pub fn get_idevid_template( + detect_str: &str, template_str: &str, asym_alg_str: &str, name_alg_str: &str, @@ -114,6 +118,11 @@ pub fn get_idevid_template( (AsymmetricAlgorithm, HashingAlgorithm), AlgorithmError, > { + let template_str = if ["", "detect", "default"].contains(&template_str) { + detect_str + } else { + template_str + }; let (asym_alg, name_alg) = match template_str { "H-1" => (AsymmetricAlgorithm::Rsa, HashingAlgorithm::Sha256), "H-2" => (AsymmetricAlgorithm::Ecc, HashingAlgorithm::Sha256),