Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect IDevID/IAK template from certificates #689

Merged
merged 1 commit into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
}
}

/// 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()),

Check warning on line 149 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L147-L149

Added lines #L147 - L149 were not covered by tests
},
cert_id if cert_id == id_rsa_pss => match cert.public_key()?.bits() {
2048 => Ok("H-1".to_string()),
_ => Ok("".to_string()),

Check warning on line 153 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L153

Added line #L153 was not covered by tests
},
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()),

Check warning on line 158 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L155-L158

Added lines #L155 - L158 were not covered by tests
},
384 => Ok("H-3".to_string()),
521 => Ok("H-4".to_string()),
_ => Ok("".to_string()),

Check warning on line 162 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L160-L162

Added lines #L160 - L162 were not covered by tests
},
_ => Err(Error::Other(
"Certificate does not seem to have an RSA or EC key".to_string(),
)),

Check warning on line 166 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L164-L166

Added lines #L164 - L166 were not covered by tests
}
}

/// 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 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

Check warning on line 124 in keylime/src/tpm.rs

View check run for this annotation

Codecov / codecov/patch

keylime/src/tpm.rs#L124

Added line #L124 was not covered by tests
};
let (asym_alg, name_alg) = match template_str {
"H-1" => (AsymmetricAlgorithm::Rsa, HashingAlgorithm::Sha256),
"H-2" => (AsymmetricAlgorithm::Ecc, HashingAlgorithm::Sha256),
Expand Down
Loading