Skip to content

Commit

Permalink
Initial PR to add support for IDevID and IAK
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 Sep 25, 2023
1 parent 626a1cc commit 5c43ca8
Show file tree
Hide file tree
Showing 6 changed files with 707 additions and 20 deletions.
15 changes: 15 additions & 0 deletions keylime-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,21 @@ 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.
# Choosing a template will override the name and asymmetric algorithm choices.
# 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_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_asymmetric_alg = "rsa"
iak_idevid_name_alg = "sha256"
iak_idevid_template = ""

# Use this option to state the existing TPM ownerpassword.
# This option should be set only when a password is set for the Endorsement
# Hierarchy (e.g. via "tpm2_changeauth -c e").
Expand Down
64 changes: 64 additions & 0 deletions keylime-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ pub static DEFAULT_TPM_HASH_ALG: &str = "sha256";
pub static DEFAULT_TPM_ENCRYPTION_ALG: &str = "rsa";
pub static DEFAULT_TPM_SIGNING_ALG: &str = "rsassa";
pub static DEFAULT_EK_HANDLE: &str = "generate";
pub static DEFAULT_ENABLE_IAK_IDEVID: bool = true;
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 = "";
pub static DEFAULT_RUN_AS: &str = "keylime:tss";
pub static DEFAULT_AGENT_DATA_PATH: &str = "agent_data.json";
pub static DEFAULT_CONFIG: &str = "/etc/keylime/agent.conf";
Expand Down Expand Up @@ -94,6 +98,10 @@ pub(crate) struct EnvConfig {
pub tpm_encryption_alg: Option<String>,
pub tpm_signing_alg: Option<String>,
pub ek_handle: Option<String>,
pub enable_iak_idevid: Option<bool>,
pub iak_idevid_asymmetric_alg: Option<String>,
pub iak_idevid_name_alg: Option<String>,
pub iak_idevid_template: Option<String>,
pub run_as: Option<String>,
pub agent_data_path: Option<String>,
}
Expand Down Expand Up @@ -132,6 +140,10 @@ pub(crate) struct AgentConfig {
pub tpm_encryption_alg: String,
pub tpm_signing_alg: String,
pub ek_handle: String,
pub enable_iak_idevid: bool,
pub iak_idevid_asymmetric_alg: String,
pub iak_idevid_name_alg: String,
pub iak_idevid_template: String,
pub run_as: String,
pub agent_data_path: String,
}
Expand Down Expand Up @@ -274,6 +286,30 @@ impl EnvConfig {
if let Some(ref v) = self.ek_handle {
_ = agent.insert("ek_handle".to_string(), v.to_string().into());
}
if let Some(ref v) = self.enable_iak_idevid {
_ = agent.insert(
"enable_iak_idevid".to_string(),
v.to_string().into(),
);
}
if let Some(ref v) = self.iak_idevid_asymmetric_alg {
_ = agent.insert(
"iak_idevid_asymmetric_alg".to_string(),
v.to_string().into(),
);
}
if let Some(ref v) = self.iak_idevid_name_alg {
_ = agent.insert(
"iak_idevid_name_alg".to_string(),
v.to_string().into(),
);
}
if let Some(ref v) = self.iak_idevid_template {
_ = agent.insert(
"iak_idevid_template".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 @@ -438,6 +474,22 @@ impl Source for KeylimeConfig {
"ek_handle".to_string(),
self.agent.ek_handle.to_string().into(),
);
_ = m.insert(
"enable_iak_idevid".to_string(),
self.agent.enable_iak_idevid.into(),
);
_ = m.insert(
"iak_idevid_asymmetric_alg".to_string(),
self.agent.iak_idevid_asymmetric_alg.to_string().into(),
);
_ = m.insert(
"iak_idevid_name_alg".to_string(),
self.agent.iak_idevid_name_alg.to_string().into(),
);
_ = m.insert(
"iak_idevid_template".to_string(),
self.agent.iak_idevid_template.to_string().into(),
);
_ = m.insert(
"run_as".to_string(),
self.agent.run_as.to_string().into(),
Expand Down Expand Up @@ -504,6 +556,11 @@ impl Default for AgentConfig {
run_as,
tpm_ownerpassword: DEFAULT_TPM_OWNERPASSWORD.to_string(),
ek_handle: DEFAULT_EK_HANDLE.to_string(),
enable_iak_idevid: DEFAULT_ENABLE_IAK_IDEVID,
iak_idevid_asymmetric_alg: DEFAULT_IAK_IDEVID_ASYMMETRIC_ALG
.to_string(),
iak_idevid_name_alg: DEFAULT_IAK_IDEVID_NAME_ALG.to_string(),
iak_idevid_template: DEFAULT_IAK_IDEVID_TEMPLATE.to_string(),
}
}
}
Expand Down Expand Up @@ -971,6 +1028,13 @@ mod tests {
("TPM_ENCRYPTION_ALG", "override_tpm_encryption_alg"),
("TPM_SIGNING_ALG", "override_tpm_signing_alg"),
("EK_HANDLE", "override_ek_handle"),
("ENABLE_IAK_IDEVID", "true"),
(
"IAK_IDEVID_ASYMMETRIC_ALG",
"override_iak_idevid_asymmetric_alg",
),
("IAK_IDEVID_NAME_ALG", "override_iak_idevid_name_alg"),
("IAK_IDEVID_TEMPLATE", "override_iak_idevid_template"),
("RUN_AS", "override_run_as"),
("AGENT_DATA_PATH", "override_agent_data_path"),
]);
Expand Down
108 changes: 94 additions & 14 deletions keylime-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ use tokio::{
};
use tss_esapi::{
handles::KeyHandle,
interface_types::algorithm::AsymmetricAlgorithm,
interface_types::algorithm::{AsymmetricAlgorithm, HashingAlgorithm},
interface_types::resource_handles::Hierarchy,
structures::{Auth, PublicBuffer},
structures::{Auth, Data, Digest, MaxBuffer, PublicBuffer},
traits::Marshall,
Context,
};
Expand Down Expand Up @@ -278,6 +278,24 @@ 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)
};

// Gather EK values and certs
let ek_result = match config.agent.ek_handle.as_ref() {
"" => ctx.create_ek(tpm_encryption_alg, None)?,
Expand Down Expand Up @@ -385,6 +403,26 @@ 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]
)?;
info!("AK certified with IAK.");

// // For debugging certify(), the following checks the generated signature
// let max_b = MaxBuffer::try_from(attest.clone().marshall()?)?;
// let (hashed_attest, _) = ctx.inner.hash(max_b, HashingAlgorithm::Sha256, Hierarchy::Endorsement,)?;
// println!("{:?}", hashed_attest);
// println!("{:?}", signature);
// println!("{:?}", ctx.inner.verify_signature(iak.as_ref().unwrap().handle, hashed_attest, signature.clone())?); //#[allow_ci]
(Some(attest), Some(signature))
} else {
(None, None)
};

// Generate key pair for secure transmission of u, v keys. The u, v
// keys are two halves of the key used to decrypt the workload after
// the Identity and Integrity Quotes sent by the agent are validated
Expand Down Expand Up @@ -497,18 +535,60 @@ async fn main() -> Result<()> {

{
// Request keyblob material
let keyblob = registrar_agent::do_register_agent(
config.agent.registrar_ip.as_ref(),
config.agent.registrar_port,
&agent_uuid,
&PublicBuffer::try_from(ek_result.public.clone())?.marshall()?,
ek_result.ek_cert,
&PublicBuffer::try_from(ak.public)?.marshall()?,
mtls_cert,
config.agent.contact_ip.as_ref(),
config.agent.contact_port,
)
.await?;
let keyblob = if config.agent.enable_iak_idevid {
let (Some(iak), Some(idevid), Some(attest), Some(signature)) =
(iak, idevid, attest, signature)
else {
error!(
"IDevID and IAK are enabled but could not be generated"
);
return Err(Error::Configuration(
"IDevID and IAK are enabled but could not be generated"
.to_string(),
));
};
registrar_agent::do_register_agent(
config.agent.registrar_ip.as_ref(),
config.agent.registrar_port,
&agent_uuid,
&PublicBuffer::try_from(ek_result.public.clone())?
.marshall()?,
ek_result.ek_cert,
&PublicBuffer::try_from(ak.public)?.marshall()?,
Some(
&PublicBuffer::try_from(iak.public.clone())?
.marshall()?,
),
Some(
&PublicBuffer::try_from(idevid.public.clone())?
.marshall()?,
),
Some(attest.marshall()?),
Some(signature.marshall()?),
mtls_cert,
config.agent.contact_ip.as_ref(),
config.agent.contact_port,
)
.await?
} else {
registrar_agent::do_register_agent(
config.agent.registrar_ip.as_ref(),
config.agent.registrar_port,
&agent_uuid,
&PublicBuffer::try_from(ek_result.public.clone())?
.marshall()?,
ek_result.ek_cert,
&PublicBuffer::try_from(ak.public)?.marshall()?,
None,
None,
None,
None,
mtls_cert,
config.agent.contact_ip.as_ref(),
config.agent.contact_port,
)
.await?
};

info!("SUCCESS: Agent {} registered", &agent_uuid);

Expand Down
40 changes: 40 additions & 0 deletions keylime-agent/src/registrar_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ struct Register<'a> {
ek_tpm: &'a [u8],
#[serde(serialize_with = "serialize_as_base64")]
aik_tpm: &'a [u8],
#[serde(
serialize_with = "serialize_option_base64",
skip_serializing_if = "Option::is_none"
)]
iak_tpm: Option<&'a [u8]>,
#[serde(
serialize_with = "serialize_option_base64",
skip_serializing_if = "Option::is_none"
)]
idevid_tpm: Option<&'a [u8]>,
#[serde(
serialize_with = "serialize_maybe_base64",
skip_serializing_if = "Option::is_none"
)]
iak_attest: Option<Vec<u8>>,
#[serde(
serialize_with = "serialize_maybe_base64",
skip_serializing_if = "Option::is_none"
)]
iak_sign: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
mtls_cert: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -94,6 +114,10 @@ pub(crate) async fn do_register_agent(
ek_tpm: &[u8],
ekcert: Option<Vec<u8>>,
aik_tpm: &[u8],
iak_tpm: Option<&[u8]>,
idevid_tpm: Option<&[u8]>,
iak_attest: Option<Vec<u8>>,
iak_sign: Option<Vec<u8>>,
mtls_cert_x509: Option<&X509>,
ip: &str,
port: u32,
Expand All @@ -113,6 +137,10 @@ pub(crate) async fn do_register_agent(
ekcert,
ek_tpm,
aik_tpm,
iak_tpm,
idevid_tpm,
iak_attest,
iak_sign,
mtls_cert,
ip,
port: Some(port),
Expand Down Expand Up @@ -195,6 +223,10 @@ mod tests {
&mock_data,
Some(mock_data.to_vec()),
&mock_data,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down Expand Up @@ -237,6 +269,10 @@ mod tests {
&mock_data,
None,
&mock_data,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down Expand Up @@ -275,6 +311,10 @@ mod tests {
&mock_data,
Some(mock_data.to_vec()),
&mock_data,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down
15 changes: 15 additions & 0 deletions keylime-agent/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ where
}
}

pub(crate) fn serialize_option_base64<S>(
value: &Option<&[u8]>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match *value {
Some(value) => {
serializer.serialize_str(&general_purpose::STANDARD.encode(value))
}
None => serializer.serialize_none(),
}
}

pub(crate) fn deserialize_maybe_base64<'de, D>(
deserializer: D,
) -> Result<Option<Vec<u8>>, D::Error>
Expand Down
Loading

0 comments on commit 5c43ca8

Please sign in to comment.