Skip to content
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
14 changes: 12 additions & 2 deletions keylime-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,10 @@ tpm_signing_alg = "rsassa"
# To override ek_handle, set KEYLIME_AGENT_EK_HANDLE environment variable.
ek_handle = "generate"

# Enable IDevID and IAK usage and set their algorithms.
# Enable IDevID and IAK usage
enable_iak_idevid = false

# Select IDevID and IAK templates or algorithms for regenerating the keys.
# 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:
Expand All @@ -237,12 +240,19 @@ ek_handle = "generate"
# 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
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"

# Alternatively if the keys are persisted, provide the handles for their location below, and optionally their passwords.
# If handles are provided, they will take priority over templates/algorithms selected above.
# To use a hex password, use the prefix "hex:" at the start of the password.
idevid_password = ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are adding new configuration options, do not forget to make a PR to add the configuration files to the configuration upgrade template.

I'll create an issue when we merge this to remind you.

idevid_handle = ""

iak_password = ""
iak_handle = ""

# The name of the file containing the X509 IAK certificate.
# If set as "default", the "iak-cert.crt" value is used
Expand Down
51 changes: 51 additions & 0 deletions keylime-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ pub static DEFAULT_ENABLE_IAK_IDEVID: bool = false;
pub static DEFAULT_IAK_IDEVID_ASYMMETRIC_ALG: &str = "rsa";
pub static DEFAULT_IAK_IDEVID_NAME_ALG: &str = "sha256";
pub static DEFAULT_IAK_IDEVID_TEMPLATE: &str = "H-1";
pub static DEFAULT_IDEVID_PASSWORD: &str = "";
pub static DEFAULT_IAK_PASSWORD: &str = "";
pub static DEFAULT_IDEVID_HANDLE: &str = "";
pub static DEFAULT_IAK_HANDLE: &str = "";
pub static DEFAULT_RUN_AS: &str = "keylime:tss";
pub static DEFAULT_AGENT_DATA_PATH: &str = "agent_data.json";
pub static DEFAULT_IMA_ML_PATH: &str =
Expand Down Expand Up @@ -111,6 +115,10 @@ pub(crate) struct EnvConfig {
pub iak_idevid_asymmetric_alg: Option<String>,
pub iak_idevid_name_alg: Option<String>,
pub iak_idevid_template: Option<String>,
pub idevid_password: Option<String>,
pub iak_password: Option<String>,
pub idevid_handle: Option<String>,
pub iak_handle: Option<String>,
pub run_as: Option<String>,
pub agent_data_path: Option<String>,
pub ima_ml_path: Option<String>,
Expand Down Expand Up @@ -157,6 +165,10 @@ pub(crate) struct AgentConfig {
pub iak_idevid_asymmetric_alg: String,
pub iak_idevid_name_alg: String,
pub iak_idevid_template: String,
pub idevid_password: String,
pub iak_password: String,
pub idevid_handle: String,
pub iak_handle: String,
pub run_as: String,
pub agent_data_path: String,
pub ima_ml_path: String,
Expand Down Expand Up @@ -328,6 +340,21 @@ impl EnvConfig {
v.to_string().into(),
);
}
if let Some(ref v) = self.idevid_password {
_ = agent
.insert("idevid_password".to_string(), v.to_string().into());
}
if let Some(ref v) = self.iak_password {
_ = agent
.insert("iak_password".to_string(), v.to_string().into());
}
if let Some(ref v) = self.idevid_handle {
_ = agent
.insert("idevid_handle".to_string(), v.to_string().into());
}
if let Some(ref v) = self.iak_handle {
_ = agent.insert("iak_handle".to_string(), v.to_string().into());
}
if let Some(ref v) = self.run_as {
_ = agent.insert("run_as".to_string(), v.to_string().into());
}
Expand Down Expand Up @@ -525,6 +552,22 @@ impl Source for KeylimeConfig {
"iak_idevid_template".to_string(),
self.agent.iak_idevid_template.to_string().into(),
);
_ = m.insert(
"idevid_password".to_string(),
self.agent.idevid_password.to_string().into(),
);
_ = m.insert(
"iak_password".to_string(),
self.agent.iak_password.to_string().into(),
);
_ = m.insert(
"idevid_handle".to_string(),
self.agent.idevid_handle.to_string().into(),
);
_ = m.insert(
"iak_handle".to_string(),
self.agent.iak_handle.to_string().into(),
);
_ = m.insert(
"run_as".to_string(),
self.agent.run_as.to_string().into(),
Expand Down Expand Up @@ -606,6 +649,10 @@ impl Default for AgentConfig {
.to_string(),
iak_idevid_name_alg: DEFAULT_IAK_IDEVID_NAME_ALG.to_string(),
iak_idevid_template: DEFAULT_IAK_IDEVID_TEMPLATE.to_string(),
idevid_password: DEFAULT_IDEVID_PASSWORD.to_string(),
iak_password: DEFAULT_IAK_PASSWORD.to_string(),
idevid_handle: DEFAULT_IDEVID_HANDLE.to_string(),
iak_handle: DEFAULT_IAK_HANDLE.to_string(),
ima_ml_path: "default".to_string(),
measuredboot_ml_path: "default".to_string(),
}
Expand Down Expand Up @@ -1124,6 +1171,10 @@ mod tests {
),
("IAK_IDEVID_NAME_ALG", "override_iak_idevid_name_alg"),
("IAK_IDEVID_TEMPLATE", "override_iak_idevid_template"),
("IDEVID_PASSWORD", "override_idevid_password"),
("IAK_PASSWORD", "override_iak_password"),
("IDEVID_HANDLE", "override_idevid_handle"),
("IAK_HANDLE", "override_iak_handle"),
("RUN_AS", "override_run_as"),
("AGENT_DATA_PATH", "override_agent_data_path"),
("IMA_ML_PATH", "override_ima_ml_path"),
Expand Down
50 changes: 39 additions & 11 deletions keylime-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,9 @@ 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
/// 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(
Expand All @@ -392,31 +393,58 @@ async fn main() -> Result<()> {
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())?;
/// 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.as_mut().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 cert could not be used".to_string(),
"IAK/IDevID enabled but IDevID cert could not be used"
.to_string(),
))?,
idevid.clone().public,
)? {
info!("Regenerated IDevID matches certificate.");
info!("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.");
/// 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 cert could not be used".to_string(),
"IAK/IDevID enabled but IAK cert could not be used"
.to_string(),
))?,
iak.clone().public,
)? {
info!("Regenerated IAK matches certificate.");
info!("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()));
Expand Down
106 changes: 105 additions & 1 deletion keylime/src/tpm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use tss_esapi::{
structure_tags::AttestationType,
},
structures::{
Attest, AttestInfo, Data, Digest, DigestValues, EccParameter,
Attest, AttestInfo, Auth, Data, Digest, DigestValues, EccParameter,
EccPoint, EccScheme, EncryptedSecret, HashScheme, IdObject,
KeyDerivationFunctionScheme, Name, PcrSelectionList,
PcrSelectionListBuilder, PcrSlot, PublicBuilder,
Expand Down Expand Up @@ -140,6 +140,13 @@ pub enum TpmError {
source: tss_esapi::Error,
},

/// Error setting auth for persistent TPM handle
#[error("Error setting auth for persistent TPM handle {handle}")]
TSSHandleSetAuthError {
handle: String,
source: tss_esapi::Error,
},

/// Error returned in case of error creating new Primary Key
#[error("Error creating primary key")]
TSSCreatePrimaryError { source: tss_esapi::Error },
Expand Down Expand Up @@ -339,6 +346,10 @@ pub enum TpmError {
#[error("base64 decode error")]
Base64Decode(#[from] base64::DecodeError),

/// Hex decoding error
#[error("hex decode error")]
HexDecodeError(String),

/// Malformed PCR selection mask
#[error("Malformed PCR selection mask: {0}")]
MalformedPCRSelectionMask(String),
Expand Down Expand Up @@ -613,6 +624,99 @@ impl Context {
Ok(ak_handle)
}

/// Load a key handle from a string of the handle location
/// If a password is supplied, authorise the handle
/// # Arguments
///
/// `handle` : The string of the handle, eg. from config
/// `password` ; The string password, to be converted to hex if there is the "hex:" prefix
///
/// # Return
/// The corresponding KeyHandle, or a TPMError
fn get_key_handle(
&mut self,
handle: &str,
password: &str,
) -> Result<KeyHandle> {
let handle = u32::from_str_radix(handle.trim_start_matches("0x"), 16)
.map_err(|source| TpmError::NumParse {
origin: handle.to_string(),
source,
})?;
let key_handle: KeyHandle = self
.inner
.tr_from_tpm_public(TpmHandle::Persistent(
PersistentTpmHandle::new(handle).map_err(|source| {
TpmError::TSSNewPersistentHandleError {
handle: handle.to_string(),
source,
}
})?,
))
.map_err(|source| TpmError::TSSHandleFromPersistentHandleError {
handle: handle.to_string(),
source,
})?
.into();
if !password.is_empty() {
let auth = if password.starts_with("hex:") {
let (_, hex_password) = password.split_at(4);
let decoded_password =
hex::decode(hex_password).map_err(|_| {
TpmError::HexDecodeError(
"Hex decode error for identity auth value."
.to_string(),
)
})?;
Auth::try_from(decoded_password)?
} else {
Auth::try_from(password.as_bytes())?
};
self.as_mut().tr_set_auth(key_handle.into(), auth).map_err(
|source| TpmError::TSSHandleSetAuthError {
handle: handle.to_string(),
source,
},
)?;
};

Ok(key_handle)
}

/// Create an IDevID object from one persisted in TPM using its handle
pub fn idevid_from_handle(
&mut self,
handle: &str,
password: &str,
) -> Result<IDevIDResult> {
let idevid_handle = self.get_key_handle(handle, password)?;
let (idevid_pub, _, _) = self
.inner
.read_public(idevid_handle)
.map_err(|source| TpmError::TSSReadPublicError { source })?;
Ok(IDevIDResult {
public: idevid_pub,
handle: idevid_handle,
})
}

/// Create an IAK object from one persisted in TPM using its handle
pub fn iak_from_handle(
&mut self,
handle: &str,
password: &str,
) -> Result<IAKResult> {
let iak_handle = self.get_key_handle(handle, password)?;
let (iak_pub, _, _) = self
.inner
.read_public(iak_handle)
.map_err(|source| TpmError::TSSReadPublicError { source })?;
Ok(IAKResult {
public: iak_pub,
handle: iak_handle,
})
}

/// Creates an IDevID
pub fn create_idevid(
&mut self,
Expand Down