Skip to content

Commit

Permalink
detect idevid template from certificates
Browse files Browse the repository at this point in the history
Signed-off-by: Isaac Matthews <[email protected]>
  • Loading branch information
Isaac-Matthews authored and ansasaki committed Jan 15, 2024
1 parent 10a3e32 commit c649ae0
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 52 deletions.
10 changes: 6 additions & 4 deletions keylime-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions keylime-agent/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
// 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,
Expand Down
97 changes: 50 additions & 47 deletions keylime-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<X509>;
let idevid_cert: Option<X509>;
// 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() {
"" => {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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() {
Expand Down
11 changes: 10 additions & 1 deletion keylime/src/tpm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,24 @@ 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,
) -> std::result::Result<
(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),
Expand Down

0 comments on commit c649ae0

Please sign in to comment.