Skip to content

Commit 33d624d

Browse files
Add certificates and certificate checking for IDevID and IAK keys (#669)
* Add certificates and certificate checking for IDevID and IAK keys Signed-off-by: Isaac Matthews <[email protected]>
1 parent 9c7c6fa commit 33d624d

File tree

5 files changed

+228
-2
lines changed

5 files changed

+228
-2
lines changed

keylime-agent.conf

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,23 @@ ek_handle = "generate"
240240
enable_iak_idevid = false
241241
iak_idevid_asymmetric_alg = "rsa"
242242
iak_idevid_name_alg = "sha256"
243-
iak_idevid_template = ""
243+
iak_idevid_template = "H-1"
244+
245+
# The name of the file containing the X509 IAK certificate.
246+
# If set as "default", the "iak-cert.crt" value is used
247+
# If a relative path is set, it will be considered relative from the keylime_dir.
248+
# If an absolute path is set, it is used without change.
249+
#
250+
# To override iak_cert, set KEYLIME_AGENT_IAK_CERT environment variable.
251+
iak_cert = "default"
252+
253+
# The name of the file containing the X509 IDevID certificate.
254+
# If set as "default", the "idevid-cert.crt" value is used
255+
# If a relative path is set, it will be considered relative from the keylime_dir.
256+
# If an absolute path is set, it is used without change.
257+
#
258+
# To override idevid_cert, set KEYLIME_AGENT_IDEVID_CERT environment variable.
259+
idevid_cert = "default"
244260

245261
# Use this option to state the existing TPM ownerpassword.
246262
# This option should be set only when a password is set for the Endorsement

keylime-agent/src/config.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub static DEFAULT_ENABLE_AGENT_MTLS: bool = true;
3131
pub static DEFAULT_KEYLIME_DIR: &str = "/var/lib/keylime";
3232
pub static DEFAULT_SERVER_KEY: &str = "server-private.pem";
3333
pub static DEFAULT_SERVER_CERT: &str = "server-cert.crt";
34+
pub static DEFAULT_IAK_CERT: &str = "iak-cert.crt";
35+
pub static DEFAULT_IDEVID_CERT: &str = "idevid-cert.crt";
3436
pub static DEFAULT_SERVER_KEY_PASSWORD: &str = "";
3537
// The DEFAULT_TRUSTED_CLIENT_CA is relative from KEYLIME_DIR
3638
pub static DEFAULT_TRUSTED_CLIENT_CA: &str = "cv_ca/cacert.crt";
@@ -58,7 +60,7 @@ pub static DEFAULT_EK_HANDLE: &str = "generate";
5860
pub static DEFAULT_ENABLE_IAK_IDEVID: bool = true;
5961
pub static DEFAULT_IAK_IDEVID_ASYMMETRIC_ALG: &str = "rsa";
6062
pub static DEFAULT_IAK_IDEVID_NAME_ALG: &str = "sha256";
61-
pub static DEFAULT_IAK_IDEVID_TEMPLATE: &str = "";
63+
pub static DEFAULT_IAK_IDEVID_TEMPLATE: &str = "H-1";
6264
pub static DEFAULT_RUN_AS: &str = "keylime:tss";
6365
pub static DEFAULT_AGENT_DATA_PATH: &str = "agent_data.json";
6466
pub static DEFAULT_CONFIG: &str = "/etc/keylime/agent.conf";
@@ -78,6 +80,8 @@ pub(crate) struct EnvConfig {
7880
pub keylime_dir: Option<String>,
7981
pub server_key: Option<String>,
8082
pub server_cert: Option<String>,
83+
pub iak_cert: Option<String>,
84+
pub idevid_cert: Option<String>,
8185
pub server_key_password: Option<String>,
8286
pub trusted_client_ca: Option<String>,
8387
pub enc_keyname: Option<String>,
@@ -120,6 +124,8 @@ pub(crate) struct AgentConfig {
120124
pub keylime_dir: String,
121125
pub server_key: String,
122126
pub server_cert: String,
127+
pub iak_cert: String,
128+
pub idevid_cert: String,
123129
pub server_key_password: String,
124130
pub trusted_client_ca: String,
125131
pub enc_keyname: String,
@@ -199,6 +205,12 @@ impl EnvConfig {
199205
if let Some(ref v) = self.server_cert {
200206
_ = agent.insert("server_cert".to_string(), v.to_string().into());
201207
}
208+
if let Some(ref v) = self.iak_cert {
209+
_ = agent.insert("iak_cert".to_string(), v.to_string().into());
210+
}
211+
if let Some(ref v) = self.idevid_cert {
212+
_ = agent.insert("idevid_cert".to_string(), v.to_string().into());
213+
}
202214
if let Some(ref v) = self.trusted_client_ca {
203215
_ = agent.insert(
204216
"trusted_client_ca".to_string(),
@@ -395,6 +407,14 @@ impl Source for KeylimeConfig {
395407
"server_cert".to_string(),
396408
self.agent.server_cert.to_string().into(),
397409
);
410+
_ = m.insert(
411+
"iak_cert".to_string(),
412+
self.agent.iak_cert.to_string().into(),
413+
);
414+
_ = m.insert(
415+
"idevid_cert".to_string(),
416+
self.agent.idevid_cert.to_string().into(),
417+
);
398418
_ = m.insert(
399419
"trusted_client_ca".to_string(),
400420
self.agent.trusted_client_ca.to_string().into(),
@@ -544,6 +564,8 @@ impl Default for AgentConfig {
544564
server_key: "default".to_string(),
545565
server_key_password: DEFAULT_SERVER_KEY_PASSWORD.to_string(),
546566
server_cert: "default".to_string(),
567+
iak_cert: "default".to_string(),
568+
idevid_cert: "default".to_string(),
547569
trusted_client_ca: "default".to_string(),
548570
revocation_actions: DEFAULT_REVOCATION_ACTIONS.to_string(),
549571
revocation_actions_dir: DEFAULT_REVOCATION_ACTIONS_DIR
@@ -724,6 +746,20 @@ fn config_translate_keywords(
724746
.collect::<Vec<_>>()
725747
.join(", ");
726748

749+
let mut iak_cert = config_get_file_path(
750+
"iak_cert",
751+
&config.agent.iak_cert,
752+
keylime_dir,
753+
DEFAULT_IAK_CERT,
754+
);
755+
756+
let mut idevid_cert = config_get_file_path(
757+
"idevid_cert",
758+
&config.agent.idevid_cert,
759+
keylime_dir,
760+
DEFAULT_IDEVID_CERT,
761+
);
762+
727763
let ek_handle = match config.agent.ek_handle.as_ref() {
728764
"generate" => "".to_string(),
729765
"" => "".to_string(),
@@ -764,6 +800,8 @@ fn config_translate_keywords(
764800
uuid,
765801
server_key,
766802
server_cert,
803+
iak_cert,
804+
idevid_cert,
767805
trusted_client_ca,
768806
ek_handle,
769807
agent_data_path,

keylime-agent/src/crypto.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use openssl::{
1818
x509::store::X509StoreBuilder,
1919
x509::{X509Name, X509},
2020
};
21+
use picky_asn1_x509::SubjectPublicKeyInfo;
2122
use std::{
2223
fs::{read_to_string, set_permissions, File, Permissions},
2324
io::{Read, Write},
@@ -30,6 +31,19 @@ use crate::{
3031
Error, Result, AES_128_KEY_LEN, AES_256_KEY_LEN, AES_BLOCK_SIZE,
3132
};
3233

34+
// Read a X509 cert in DER format from path
35+
pub(crate) fn load_x509_der(input_cert_path: &Path) -> Result<X509> {
36+
let contents = std::fs::read(input_cert_path).map_err(Error::from)?;
37+
38+
X509::from_der(&contents).map_err(Error::Crypto)
39+
}
40+
41+
pub(crate) fn load_x509_pem(input_cert_path: &Path) -> Result<X509> {
42+
let contents = std::fs::read(input_cert_path).map_err(Error::from)?;
43+
44+
X509::from_pem(&contents).map_err(Error::Crypto)
45+
}
46+
3347
// Read a X509 cert or cert chain and outputs the first certificate
3448
pub(crate) fn load_x509(input_cert_path: &Path) -> Result<X509> {
3549
let mut cert_chain = load_x509_cert_chain(input_cert_path)?;
@@ -75,6 +89,54 @@ pub(crate) fn write_x509(cert: &X509, file_path: &Path) -> Result<()> {
7589
Ok(())
7690
}
7791

92+
/// Check an x509 certificate contains a specific public key
93+
pub(crate) fn check_x509_key(
94+
cert: &X509,
95+
tpm_key: tss_esapi::structures::Public,
96+
) -> Result<bool> {
97+
// Id:RSA_PSS only added in rust-openssl from v0.10.59; remove this let and use Id::RSA_PSS after update
98+
// Id taken from https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/nid.h#4039
99+
let id_rsa_pss: Id = Id::from_raw(912);
100+
match cert.public_key()?.id() {
101+
Id::RSA => {
102+
let cert_n = cert.public_key()?.rsa()?.n().to_vec();
103+
let mut cert_n_str = format!("{:?}", cert_n);
104+
_ = cert_n_str.pop();
105+
_ = cert_n_str.remove(0);
106+
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
107+
let key_der = picky_asn1_der::to_vec(&key)?;
108+
let key_der_str = format!("{:?}", key_der);
109+
110+
Ok(key_der_str.contains(&cert_n_str))
111+
}
112+
cert_id if cert_id == id_rsa_pss => {
113+
let cert_n = cert.public_key()?.rsa()?.n().to_vec();
114+
let mut cert_n_str = format!("{:?}", cert_n);
115+
_ = cert_n_str.pop();
116+
_ = cert_n_str.remove(0);
117+
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
118+
let key_der = picky_asn1_der::to_vec(&key)?;
119+
let key_der_str = format!("{:?}", key_der);
120+
121+
Ok(key_der_str.contains(&cert_n_str))
122+
}
123+
Id::EC => {
124+
let cert_n = cert.public_key()?.ec_key()?.public_key_to_der()?;
125+
let mut cert_n_str = format!("{:?}", cert_n);
126+
_ = cert_n_str.pop();
127+
_ = cert_n_str.remove(0);
128+
let key = SubjectPublicKeyInfo::try_from(tpm_key)?;
129+
let key_der = picky_asn1_der::to_vec(&key)?;
130+
let key_der_str = format!("{:?}", key_der);
131+
132+
Ok(key_der_str.contains(&cert_n_str))
133+
}
134+
_ => Err(Error::Other(
135+
"Certificate does not seem to have an RSA or EC key".to_string(),
136+
)),
137+
}
138+
}
139+
78140
/// Read a PEM file and returns the public and private keys
79141
pub(crate) fn load_key_pair(
80142
key_path: &Path,

keylime-agent/src/main.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,88 @@ async fn main() -> Result<()> {
295295
(None, None)
296296
};
297297

298+
let iak_cert: Option<X509>;
299+
let idevid_cert: Option<X509>;
300+
// Attempt to load the IAK and IDevID certificates
301+
// Check the certificates contain the keys that have been regenerated by the TPM
302+
// If they do not there is likely a configuration issue where the template has been set to the wrong value
303+
if config.agent.enable_iak_idevid {
304+
iak_cert = match config.agent.iak_cert.as_ref() {
305+
"" => {
306+
debug!("The iak_cert option was not set in the configuration file");
307+
None
308+
}
309+
path => {
310+
let iak_path = Path::new(&path);
311+
if iak_path.exists() {
312+
debug!(
313+
"Loading IAK certificate from {}",
314+
iak_path.display()
315+
);
316+
let iakcert = match crypto::load_x509_der(iak_path) {
317+
Ok(cert) => cert,
318+
Err(error) => crypto::load_x509_pem(iak_path)?,
319+
};
320+
if crypto::check_x509_key(
321+
&iakcert,
322+
iak.clone()
323+
.expect(
324+
"IAK could not be used in cert key check.",
325+
)
326+
.public,
327+
)? {
328+
Some(iakcert)
329+
} else {
330+
error!("IAK template does not match certificate. Check template in configuration.");
331+
return Err(Error::Configuration("IAK template does not match certificate. Check template in configuration.".to_string()));
332+
}
333+
} else {
334+
debug!("Can not find IAK certificate");
335+
None
336+
}
337+
}
338+
};
339+
idevid_cert = match config.agent.idevid_cert.as_ref() {
340+
"" => {
341+
debug!("The idevid_cert option was not set in the configuration file");
342+
None
343+
}
344+
path => {
345+
let idevid_path = Path::new(&path);
346+
if idevid_path.exists() {
347+
debug!(
348+
"Loading IDevID certificate from {}",
349+
idevid_path.display()
350+
);
351+
let idevcert = match crypto::load_x509_der(idevid_path) {
352+
Ok(cert) => cert,
353+
Err(error) => crypto::load_x509_pem(idevid_path)?,
354+
};
355+
if crypto::check_x509_key(
356+
&idevcert,
357+
idevid
358+
.clone()
359+
.expect(
360+
"IDevID could not be used in cert key check.",
361+
)
362+
.public,
363+
)? {
364+
Some(idevcert)
365+
} else {
366+
error!("IDevID template does not match certificate. Check template in configuration.");
367+
return Err(Error::Configuration("IDevID template does not match certificate. Check template in configuration.".to_string()));
368+
}
369+
} else {
370+
debug!("Can not find IDevID certificate");
371+
None
372+
}
373+
}
374+
};
375+
} else {
376+
iak_cert = None;
377+
idevid_cert = None;
378+
}
379+
298380
// Gather EK values and certs
299381
let ek_result = match config.agent.ek_handle.as_ref() {
300382
"" => ctx.create_ek(tpm_encryption_alg, None)?,
@@ -562,6 +644,8 @@ async fn main() -> Result<()> {
562644
&PublicBuffer::try_from(idevid.public.clone())?
563645
.marshall()?,
564646
),
647+
idevid_cert,
648+
iak_cert,
565649
Some(attest.marshall()?),
566650
Some(signature.marshall()?),
567651
mtls_cert,
@@ -582,6 +666,8 @@ async fn main() -> Result<()> {
582666
None,
583667
None,
584668
None,
669+
None,
670+
None,
585671
mtls_cert,
586672
config.agent.contact_ip.as_ref(),
587673
config.agent.contact_port,

keylime-agent/src/registrar_agent.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ struct Register<'a> {
3232
skip_serializing_if = "Option::is_none"
3333
)]
3434
idevid_tpm: Option<&'a [u8]>,
35+
#[serde(serialize_with = "serialize_maybe_base64")]
36+
idevid_cert: Option<Vec<u8>>,
37+
#[serde(serialize_with = "serialize_maybe_base64")]
38+
iak_cert: Option<Vec<u8>>,
3539
#[serde(
3640
serialize_with = "serialize_maybe_base64",
3741
skip_serializing_if = "Option::is_none"
@@ -116,6 +120,8 @@ pub(crate) async fn do_register_agent(
116120
aik_tpm: &[u8],
117121
iak_tpm: Option<&[u8]>,
118122
idevid_tpm: Option<&[u8]>,
123+
idevid_cert_x509: Option<X509>,
124+
iak_cert_x509: Option<X509>,
119125
iak_attest: Option<Vec<u8>>,
120126
iak_sign: Option<Vec<u8>>,
121127
mtls_cert_x509: Option<&X509>,
@@ -127,6 +133,16 @@ pub(crate) async fn do_register_agent(
127133
None => Some("disabled".to_string()),
128134
};
129135

136+
let idevid_cert = match idevid_cert_x509 {
137+
Some(cert) => Some(cert.to_der()?),
138+
None => None,
139+
};
140+
141+
let iak_cert = match iak_cert_x509 {
142+
Some(cert) => Some(cert.to_der()?),
143+
None => None,
144+
};
145+
130146
let ip = if ip.is_empty() {
131147
None
132148
} else {
@@ -139,6 +155,8 @@ pub(crate) async fn do_register_agent(
139155
aik_tpm,
140156
iak_tpm,
141157
idevid_tpm,
158+
idevid_cert,
159+
iak_cert,
142160
iak_attest,
143161
iak_sign,
144162
mtls_cert,
@@ -227,6 +245,8 @@ mod tests {
227245
None,
228246
None,
229247
None,
248+
None,
249+
None,
230250
Some(&cert),
231251
"",
232252
0,
@@ -273,6 +293,8 @@ mod tests {
273293
None,
274294
None,
275295
None,
296+
None,
297+
None,
276298
Some(&cert),
277299
"",
278300
0,
@@ -315,6 +337,8 @@ mod tests {
315337
None,
316338
None,
317339
None,
340+
None,
341+
None,
318342
Some(&cert),
319343
"",
320344
0,

0 commit comments

Comments
 (0)