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

Add certificates and certificate checking for IDevID and IAK keys #669

Merged
merged 5 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
18 changes: 17 additions & 1 deletion keylime-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,23 @@ ek_handle = "generate"
enable_iak_idevid = false
iak_idevid_asymmetric_alg = "rsa"
iak_idevid_name_alg = "sha256"
iak_idevid_template = ""
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
# If a relative path is set, it will be considered relative from the keylime_dir.
# If an absolute path is set, it is used without change.
#
# To override iak_cert, set KEYLIME_AGENT_IAK_CERT environment variable.
iak_cert = "default"

# The name of the file containing the X509 IDevID certificate.
# If set as "default", the "idevid-cert.crt" value is used
# If a relative path is set, it will be considered relative from the keylime_dir.
# If an absolute path is set, it is used without change.
#
# To override idevid_cert, set KEYLIME_AGENT_IDEVID_CERT environment variable.
idevid_cert = "default"

# Use this option to state the existing TPM ownerpassword.
# This option should be set only when a password is set for the Endorsement
Expand Down
40 changes: 39 additions & 1 deletion keylime-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
pub static DEFAULT_KEYLIME_DIR: &str = "/var/lib/keylime";
pub static DEFAULT_SERVER_KEY: &str = "server-private.pem";
pub static DEFAULT_SERVER_CERT: &str = "server-cert.crt";
pub static DEFAULT_IAK_CERT: &str = "iak-cert.crt";
pub static DEFAULT_IDEVID_CERT: &str = "idevid-cert.crt";
pub static DEFAULT_SERVER_KEY_PASSWORD: &str = "";
// The DEFAULT_TRUSTED_CLIENT_CA is relative from KEYLIME_DIR
pub static DEFAULT_TRUSTED_CLIENT_CA: &str = "cv_ca/cacert.crt";
Expand Down Expand Up @@ -58,7 +60,7 @@
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_IAK_IDEVID_TEMPLATE: &str = "H-1";
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 All @@ -78,6 +80,8 @@
pub keylime_dir: Option<String>,
pub server_key: Option<String>,
pub server_cert: Option<String>,
pub iak_cert: Option<String>,
pub idevid_cert: Option<String>,

Check warning on line 84 in keylime-agent/src/config.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/config.rs#L83-L84

Added lines #L83 - L84 were not covered by tests
pub server_key_password: Option<String>,
pub trusted_client_ca: Option<String>,
pub enc_keyname: Option<String>,
Expand Down Expand Up @@ -120,6 +124,8 @@
pub keylime_dir: String,
pub server_key: String,
pub server_cert: String,
pub iak_cert: String,
pub idevid_cert: String,

Check warning on line 128 in keylime-agent/src/config.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/config.rs#L127-L128

Added lines #L127 - L128 were not covered by tests
pub server_key_password: String,
pub trusted_client_ca: String,
pub enc_keyname: String,
Expand Down Expand Up @@ -199,6 +205,12 @@
if let Some(ref v) = self.server_cert {
_ = agent.insert("server_cert".to_string(), v.to_string().into());
}
if let Some(ref v) = self.iak_cert {
_ = agent.insert("iak_cert".to_string(), v.to_string().into());

Check warning on line 209 in keylime-agent/src/config.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/config.rs#L209

Added line #L209 was not covered by tests
}
if let Some(ref v) = self.idevid_cert {
_ = agent.insert("idevid_cert".to_string(), v.to_string().into());

Check warning on line 212 in keylime-agent/src/config.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/config.rs#L212

Added line #L212 was not covered by tests
}
if let Some(ref v) = self.trusted_client_ca {
_ = agent.insert(
"trusted_client_ca".to_string(),
Expand Down Expand Up @@ -395,6 +407,14 @@
"server_cert".to_string(),
self.agent.server_cert.to_string().into(),
);
_ = m.insert(
"iak_cert".to_string(),
self.agent.iak_cert.to_string().into(),
);
_ = m.insert(
"idevid_cert".to_string(),
self.agent.idevid_cert.to_string().into(),
);
_ = m.insert(
"trusted_client_ca".to_string(),
self.agent.trusted_client_ca.to_string().into(),
Expand Down Expand Up @@ -544,6 +564,8 @@
server_key: "default".to_string(),
server_key_password: DEFAULT_SERVER_KEY_PASSWORD.to_string(),
server_cert: "default".to_string(),
iak_cert: "default".to_string(),
idevid_cert: "default".to_string(),
trusted_client_ca: "default".to_string(),
revocation_actions: DEFAULT_REVOCATION_ACTIONS.to_string(),
revocation_actions_dir: DEFAULT_REVOCATION_ACTIONS_DIR
Expand Down Expand Up @@ -724,6 +746,20 @@
.collect::<Vec<_>>()
.join(", ");

let mut iak_cert = config_get_file_path(
"iak_cert",
&config.agent.iak_cert,
keylime_dir,
DEFAULT_IAK_CERT,
);

let mut idevid_cert = config_get_file_path(
"idevid_cert",
&config.agent.idevid_cert,
keylime_dir,
DEFAULT_IDEVID_CERT,
);

let ek_handle = match config.agent.ek_handle.as_ref() {
"generate" => "".to_string(),
"" => "".to_string(),
Expand Down Expand Up @@ -764,6 +800,8 @@
uuid,
server_key,
server_cert,
iak_cert,
idevid_cert,
trusted_client_ca,
ek_handle,
agent_data_path,
Expand Down
74 changes: 74 additions & 0 deletions keylime-agent/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
x509::store::X509StoreBuilder,
x509::{X509Name, X509},
};
use picky_asn1_x509::SubjectPublicKeyInfo;
use std::{
fs::{read_to_string, set_permissions, File, Permissions},
io::{Read, Write},
Expand All @@ -30,6 +31,19 @@
Error, Result, AES_128_KEY_LEN, AES_256_KEY_LEN, AES_BLOCK_SIZE,
};

// Read a X509 cert in DER format from path
pub(crate) fn load_x509_der(input_cert_path: &Path) -> Result<X509> {
let contents = std::fs::read(input_cert_path).map_err(Error::from)?;

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L35-L36

Added lines #L35 - L36 were not covered by tests

X509::from_der(&contents).map_err(Error::Crypto)
}

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L38-L39

Added lines #L38 - L39 were not covered by tests

pub(crate) fn load_x509_pem(input_cert_path: &Path) -> Result<X509> {
let contents = std::fs::read(input_cert_path).map_err(Error::from)?;

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L41-L42

Added lines #L41 - L42 were not covered by tests

X509::from_pem(&contents).map_err(Error::Crypto)
}

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L44-L45

Added lines #L44 - L45 were not covered by tests

// Read a X509 cert or cert chain and outputs the first certificate
pub(crate) fn load_x509(input_cert_path: &Path) -> Result<X509> {
let mut cert_chain = load_x509_cert_chain(input_cert_path)?;
Expand Down Expand Up @@ -75,6 +89,66 @@
Ok(())
}

/// Check an x509 certificate contains a specific public key
pub(crate) fn check_x509_key(
cert: &X509,
tpm_key: tss_esapi::structures::Public,
) -> Result<bool> {
// 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()
.unwrap() //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
.id()

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L93-L103

Added lines #L93 - L103 were not covered by tests
{
Id::RSA => {
let cert_n =
cert.public_key().unwrap().rsa().unwrap().n().to_vec(); //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
let mut cert_n_str = format!("{:?}", cert_n);
_ = cert_n_str.pop();
_ = cert_n_str.remove(0);
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
let key_der = picky_asn1_der::to_vec(&key)?;
let key_der_str = format!("{:?}", key_der);

Ok(key_der_str.contains(&cert_n_str))

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L106-L115

Added lines #L106 - L115 were not covered by tests
}
cert_id if cert_id == id_rsa_pss => {
let cert_n =
cert.public_key().unwrap().rsa().unwrap().n().to_vec(); //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
let mut cert_n_str = format!("{:?}", cert_n);
_ = cert_n_str.pop();
_ = cert_n_str.remove(0);
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
let key_der = picky_asn1_der::to_vec(&key)?;
let key_der_str = format!("{:?}", key_der);

Ok(key_der_str.contains(&cert_n_str))

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L117-L127

Added lines #L117 - L127 were not covered by tests
}
Id::EC => {
let cert_n = cert
.public_key()
.unwrap() //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
.ec_key()
.unwrap() //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
.public_key_to_der()
.unwrap(); //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
let mut cert_n_str = format!("{:?}", cert_n);
_ = cert_n_str.pop();
_ = cert_n_str.remove(0);
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
let key_der = picky_asn1_der::to_vec(&key)?;
let key_der_str = format!("{:?}", key_der);

Ok(key_der_str.contains(&cert_n_str))

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L130-L144

Added lines #L130 - L144 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 148 in keylime-agent/src/crypto.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L146-L148

Added lines #L146 - L148 were not covered by tests
}
}

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

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/crypto.rs#L150

Added line #L150 was 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
81 changes: 81 additions & 0 deletions keylime-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,83 @@ async fn main() -> Result<()> {
(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() {
"" => {
debug!("The iak_cert option was not set in the configuration file");
None
}
path => {
let iak_path = Path::new(&path);
if iak_path.exists() {
debug!(
"Loading IAK certificate from {}",
iak_path.display()
);
let iakcert = match crypto::load_x509_der(iak_path) {
Ok(cert) => cert,
Err(error) => crypto::load_x509_pem(iak_path)?,
};
if crypto::check_x509_key(
&iakcert,
iak.clone().unwrap().public, //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
)
.unwrap(/*//#[allow_ci]*/)
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
{
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()));
}
} else {
debug!("Can not find IAK certificate");
None
}
}
};
idevid_cert = match config.agent.idevid_cert.as_ref() {
"" => {
debug!("The idevid_cert option was not set in the configuration file");
None
}
path => {
let idevid_path = Path::new(&path);
if idevid_path.exists() {
debug!(
"Loading IDevID certificate from {}",
idevid_path.display()
);
let idevcert = match crypto::load_x509_der(idevid_path) {
Ok(cert) => cert,
Err(error) => crypto::load_x509_pem(idevid_path)?,
};
if crypto::check_x509_key(
&idevcert,
idevid.clone().unwrap().public, //#[allow_ci]
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
)
.unwrap(/*//#[allow_ci]*/)
ansasaki marked this conversation as resolved.
Show resolved Hide resolved
{
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()));
}
} else {
debug!("Can not find IDevID certificate");
None
}
}
};
} else {
iak_cert = None;
idevid_cert = 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 @@ -562,6 +639,8 @@ async fn main() -> Result<()> {
&PublicBuffer::try_from(idevid.public.clone())?
.marshall()?,
),
idevid_cert,
iak_cert,
Some(attest.marshall()?),
Some(signature.marshall()?),
mtls_cert,
Expand All @@ -582,6 +661,8 @@ async fn main() -> Result<()> {
None,
None,
None,
None,
None,
mtls_cert,
config.agent.contact_ip.as_ref(),
config.agent.contact_port,
Expand Down
24 changes: 24 additions & 0 deletions keylime-agent/src/registrar_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
skip_serializing_if = "Option::is_none"
)]
idevid_tpm: Option<&'a [u8]>,
#[serde(serialize_with = "serialize_maybe_base64")]
idevid_cert: Option<Vec<u8>>,
#[serde(serialize_with = "serialize_maybe_base64")]
iak_cert: Option<Vec<u8>>,
#[serde(
serialize_with = "serialize_maybe_base64",
skip_serializing_if = "Option::is_none"
Expand Down Expand Up @@ -116,6 +120,8 @@
aik_tpm: &[u8],
iak_tpm: Option<&[u8]>,
idevid_tpm: Option<&[u8]>,
idevid_cert_x509: Option<X509>,
iak_cert_x509: Option<X509>,
iak_attest: Option<Vec<u8>>,
iak_sign: Option<Vec<u8>>,
mtls_cert_x509: Option<&X509>,
Expand All @@ -127,6 +133,16 @@
None => Some("disabled".to_string()),
};

let idevid_cert = match idevid_cert_x509 {
Some(cert) => Some(cert.to_der()?),

Check warning on line 137 in keylime-agent/src/registrar_agent.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/registrar_agent.rs#L137

Added line #L137 was not covered by tests
None => None,
};

let iak_cert = match iak_cert_x509 {
Some(cert) => Some(cert.to_der()?),

Check warning on line 142 in keylime-agent/src/registrar_agent.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/registrar_agent.rs#L142

Added line #L142 was not covered by tests
None => None,
};

let ip = if ip.is_empty() {
None
} else {
Expand All @@ -139,6 +155,8 @@
aik_tpm,
iak_tpm,
idevid_tpm,
idevid_cert,
iak_cert,
iak_attest,
iak_sign,
mtls_cert,
Expand Down Expand Up @@ -227,6 +245,8 @@
None,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down Expand Up @@ -273,6 +293,8 @@
None,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down Expand Up @@ -315,6 +337,8 @@
None,
None,
None,
None,
None,
Some(&cert),
"",
0,
Expand Down
Loading