From 478eef597e57b877def88da12c22a2f6b1f2a1ed Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Wed, 18 Dec 2024 20:16:29 +0100 Subject: [PATCH] main: Support using multiple API versions for registration Use the keylime::registrar_client module instead of the registrar_agent, which is deleted. This enables the agent to communicate with a registrar using an older API version, restoring the backwards compatibility. This is part of the implementation of the enhancement proposal 114: https://github.com/keylime/enhancements/pull/115 Signed-off-by: Anderson Toshiyuki Sasaki --- Cargo.lock | 307 ++---------------- keylime-agent/Cargo.toml | 7 +- keylime-agent/src/error.rs | 17 +- keylime-agent/src/main.rs | 121 ++++---- keylime-agent/src/registrar_agent.rs | 446 --------------------------- 5 files changed, 90 insertions(+), 808 deletions(-) delete mode 100644 keylime-agent/src/registrar_agent.rs diff --git a/Cargo.lock b/Cargo.lock index 5efdf444..d939804b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand 0.8.5", + "rand", "sha1", "smallvec", "tokio", @@ -215,7 +215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", - "getrandom 0.2.7", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -279,12 +279,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "anyhow" -version = "1.0.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" - [[package]] name = "assert-json-diff" version = "2.0.2" @@ -295,17 +289,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "async-channel" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "async-trait" version = "0.1.57" @@ -340,12 +323,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -435,12 +412,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cc" version = "1.0.73" @@ -519,15 +490,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - [[package]] name = "config" version = "0.13.4" @@ -582,19 +544,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "deadpool" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" -dependencies = [ - "async-trait", - "deadpool-runtime", - "num_cpus", - "retain_mut", - "tokio", -] - [[package]] name = "deadpool" version = "0.10.0" @@ -706,21 +655,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.2.0" @@ -815,21 +749,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand 1.8.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.31" @@ -853,12 +772,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - [[package]] name = "futures-util" version = "0.3.31" @@ -887,17 +800,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.7" @@ -906,7 +808,7 @@ checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1008,17 +910,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http 0.2.8", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1038,31 +929,10 @@ dependencies = [ "bytes", "futures-util", "http 1.1.0", - "http-body 1.0.1", + "http-body", "pin-project-lite", ] -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "base64 0.13.1", - "futures-lite", - "http 0.2.8", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", -] - [[package]] name = "httparse" version = "1.8.0" @@ -1084,30 +954,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.8", - "http-body 0.4.5", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.4.1" @@ -1119,7 +965,7 @@ dependencies = [ "futures-util", "h2 0.4.7", "http 1.1.0", - "http-body 1.0.1", + "http-body", "httparse", "httpdate", "itoa", @@ -1139,8 +985,8 @@ dependencies = [ "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", + "http-body", + "hyper", "pin-project-lite", "socket2 0.5.4", "tokio", @@ -1174,21 +1020,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.5.0" @@ -1238,7 +1069,7 @@ dependencies = [ "thiserror 1.0.63", "tokio", "tss-esapi", - "wiremock 0.6.2", + "wiremock", ] [[package]] @@ -1269,7 +1100,6 @@ dependencies = [ "tokio", "tss-esapi", "uuid", - "wiremock 0.5.22", "zip", "zmq", ] @@ -1419,7 +1249,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -1525,12 +1355,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1726,19 +1550,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -1746,18 +1557,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1767,16 +1568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -1785,16 +1577,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -1840,9 +1623,9 @@ dependencies = [ "futures-core", "futures-util", "http 1.1.0", - "http-body 1.0.1", + "http-body", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "ipnet", "js-sys", @@ -1864,12 +1647,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "retain_mut" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1984,17 +1761,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror 1.0.63", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2163,7 +1929,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", - "fastrand 2.2.0", + "fastrand", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2428,7 +2194,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] [[package]] @@ -2443,7 +2208,7 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ - "getrandom 0.2.7", + "getrandom", ] [[package]] @@ -2458,12 +2223,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "want" version = "0.3.0" @@ -2474,12 +2233,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2826,28 +2579,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "wiremock" -version = "0.5.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a3a53eaf34f390dd30d7b1b078287dd05df2aa2e21a589ccb80f5c7253c2e9" -dependencies = [ - "assert-json-diff", - "async-trait", - "base64 0.21.7", - "deadpool 0.9.5", - "futures", - "futures-timer", - "http-types", - "hyper 0.14.27", - "log", - "once_cell", - "regex", - "serde", - "serde_json", - "tokio", -] - [[package]] name = "wiremock" version = "0.6.2" @@ -2857,11 +2588,11 @@ dependencies = [ "assert-json-diff", "async-trait", "base64 0.22.1", - "deadpool 0.10.0", + "deadpool", "futures", "http 1.1.0", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "log", "once_cell", diff --git a/keylime-agent/Cargo.toml b/keylime-agent/Cargo.toml index 512491c3..c684516d 100644 --- a/keylime-agent/Cargo.toml +++ b/keylime-agent/Cargo.toml @@ -33,10 +33,6 @@ thiserror.workspace = true uuid.workspace = true zip.workspace = true zmq = {version = "0.9.2", optional = true} -# wiremock was moved to be a regular dependency because optional -# dev-dependencies are not supported -# see: https://github.com/rust-lang/cargo/issues/1596 -wiremock = {version = "0.5", optional = true} [dev-dependencies] actix-rt.workspace = true @@ -44,8 +40,7 @@ actix-rt.workspace = true [features] # The features enabled by default default = [] -# this should change to dev-dependencies when we have integration testing -testing = ["wiremock"] +testing = [] # Whether the agent should be compiled with support to listen for notification # messages on ZeroMQ # diff --git a/keylime-agent/src/error.rs b/keylime-agent/src/error.rs index caccb26a..16046c1d 100644 --- a/keylime-agent/src/error.rs +++ b/keylime-agent/src/error.rs @@ -33,8 +33,12 @@ pub(crate) enum Error { DeviceIDBuilder(#[from] keylime::device_id::DeviceIDBuilderError), #[error("Reqwest error: {0}")] Reqwest(#[from] reqwest::Error), - #[error("Registrar error: received {code} from {addr}")] - Registrar { addr: String, code: u16 }, + #[error("RegistrarClient error")] + RegistrarClient(#[from] keylime::registrar_client::RegistrarClientError), + #[error("RegistrarClientBuilder error")] + RegistrarClientBuilder( + #[from] keylime::registrar_client::RegistrarClientBuilderError, + ), #[error("Serialization/deserialization error: {0}")] Serde(#[from] serde_json::Error), #[error("Permission error")] @@ -108,15 +112,6 @@ pub(crate) enum Error { impl actix_web::ResponseError for Error {} impl Error { - pub(crate) fn http_code(&self) -> Result { - match self { - Error::Registrar { addr, code } => Ok(*code), - other => Err(Error::Other(format!( - "cannot get http code for Error type {other}" - ))), - } - } - pub(crate) fn exe_code(&self) -> Result> { match self { Error::Execution(code, _) => Ok(code.to_owned()), diff --git a/keylime-agent/src/main.rs b/keylime-agent/src/main.rs index 336e19d5..a4f0bf4e 100644 --- a/keylime-agent/src/main.rs +++ b/keylime-agent/src/main.rs @@ -42,7 +42,6 @@ mod notifications_handler; mod payloads; mod permissions; mod quotes_handler; -mod registrar_agent; mod revocation; mod secure_mount; @@ -60,6 +59,7 @@ use keylime::{ device_id::{DeviceID, DeviceIDBuilder}, ima::MeasurementList, list_parser::parse_list, + registrar_client::RegistrarClientBuilder, serialization, tpm::{self, IAKResult, IDevIDResult}, }; @@ -599,7 +599,7 @@ async fn main() -> Result<()> { } }?; - mtls_cert = Some(&cert); + mtls_cert = Some(cert.clone()); ssl_context = Some(crypto::generate_tls_context( &cert, &nk_priv, @@ -612,8 +612,37 @@ async fn main() -> Result<()> { } { - // Request keyblob material - let keyblob = if config.agent.enable_iak_idevid { + // Declare here as these must live longer than the builder + let iak_pub; + let idevid_pub; + let ak_pub = &PublicBuffer::try_from(ak.public)?.marshall()?; + let ek_pub = + &PublicBuffer::try_from(ek_result.public.clone())?.marshall()?; + + // Create a RegistrarClientBuilder and set the parameters + let mut builder = RegistrarClientBuilder::new() + .ak_pub(ak_pub) + .ek_pub(ek_pub) + .enabled_api_versions( + api_versions.iter().map(|ver| ver.as_ref()).collect(), + ) + .registrar_ip(config.agent.registrar_ip.clone()) + .registrar_port(config.agent.registrar_port) + .uuid(&agent_uuid) + .ip(config.agent.contact_ip.clone()) + .port(config.agent.contact_port); + + if let Some(mtls_cert) = mtls_cert { + builder = builder.mtls_cert(mtls_cert); + } + + // If the certificate is not None add it to the builder + if let Some(ek_cert) = ek_result.ek_cert { + builder = builder.ek_cert(ek_cert); + } + + // Set the IAK/IDevID related fields, if enabled + if config.agent.enable_iak_idevid { let (Some(dev_id), Some(attest), Some(signature)) = (&device_id, attest, signature) else { @@ -625,52 +654,34 @@ async fn main() -> Result<()> { .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(dev_id.iak_pubkey.clone())? - .marshall()?, - ), - Some( - &PublicBuffer::try_from(dev_id.idevid_pubkey.clone())? - .marshall()?, - ), - dev_id.idevid_cert.clone(), - dev_id.iak_cert.clone(), - 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, - None, - None, - mtls_cert, - config.agent.contact_ip.as_ref(), - config.agent.contact_port, - ) - .await? - }; + + iak_pub = PublicBuffer::try_from(dev_id.iak_pubkey.clone())? + .marshall()?; + idevid_pub = + PublicBuffer::try_from(dev_id.idevid_pubkey.clone())? + .marshall()?; + builder = builder + .iak_attest(attest.marshall()?) + .iak_sign(signature.marshall()?) + .iak_pub(&iak_pub) + .idevid_pub(&idevid_pub); + + // If the IAK certificate was provided, set it + if let Some(iak_cert) = dev_id.iak_cert.clone() { + builder = builder.iak_cert(iak_cert); + } + + // If the IDevID certificate was provided, set it + if let Some(idevid_cert) = dev_id.idevid_cert.clone() { + builder = builder.idevid_cert(idevid_cert); + } + } + + // Build the registrar client + let mut registrar_client = builder.build().await?; + + // Request keyblob material + let keyblob = registrar_client.register_agent().await?; info!("SUCCESS: Agent {} registered", &agent_uuid); @@ -679,22 +690,18 @@ async fn main() -> Result<()> { ak_handle, ek_result.key_handle, )?; + // Flush EK if we created it if config.agent.ek_handle.is_empty() { ctx.flush_context(ek_result.key_handle.into())?; } + let mackey = general_purpose::STANDARD.encode(key.value()); let auth_tag = crypto::compute_hmac(mackey.as_bytes(), agent_uuid.as_bytes())?; let auth_tag = hex::encode(&auth_tag); - registrar_agent::do_activate_agent( - config.agent.registrar_ip.as_ref(), - config.agent.registrar_port, - &agent_uuid, - &auth_tag, - ) - .await?; + registrar_client.activate_agent(&auth_tag).await?; info!("SUCCESS: Agent {} activated", &agent_uuid); } diff --git a/keylime-agent/src/registrar_agent.rs b/keylime-agent/src/registrar_agent.rs deleted file mode 100644 index 1839d0b3..00000000 --- a/keylime-agent/src/registrar_agent.rs +++ /dev/null @@ -1,446 +0,0 @@ -use crate::error::Error; - -use crate::common::API_VERSION; -use keylime::serialization::*; -use log::*; -use openssl::x509::X509; -use serde::{Deserialize, Serialize}; -use serde_json::Number; -use std::net::IpAddr; - -#[derive(Debug, Serialize, Deserialize)] -struct Register<'a> { - #[serde(serialize_with = "serialize_maybe_base64")] - ekcert: Option>, - #[serde( - serialize_with = "serialize_as_base64", - skip_serializing_if = "is_empty" - )] - 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")] - idevid_cert: Option>, - #[serde(serialize_with = "serialize_maybe_base64")] - iak_cert: Option>, - #[serde( - serialize_with = "serialize_maybe_base64", - skip_serializing_if = "Option::is_none" - )] - iak_attest: Option>, - #[serde( - serialize_with = "serialize_maybe_base64", - skip_serializing_if = "Option::is_none" - )] - iak_sign: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - mtls_cert: Option, - #[serde(skip_serializing_if = "Option::is_none")] - ip: Option, - #[serde(skip_serializing_if = "Option::is_none")] - port: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct RegisterResponseResults { - #[serde(deserialize_with = "deserialize_maybe_base64")] - blob: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Activate<'a> { - auth_tag: &'a str, -} - -#[derive(Debug, Serialize, Deserialize)] -struct ActivateResponseResults {} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Response { - code: Number, - status: String, - results: T, -} - -pub(crate) async fn do_activate_agent( - registrar_ip: &str, - registrar_port: u32, - agent_uuid: &str, - auth_tag: &str, -) -> crate::error::Result<()> { - let data = Activate { auth_tag }; - - let remote_ip = match registrar_ip.parse::() { - Ok(addr) => { - // Add brackets if the address is IPv6 - if addr.is_ipv6() { - format!("[{registrar_ip}]") - } else { - registrar_ip.to_string() - } - } - Err(_) => { - // The registrar_ip option can also be a hostname. If it is the case, the hostname was - // already validated during configuration - registrar_ip.to_string() - } - }; - - #[cfg(test)] - let addr = format!("http://{remote_ip}:{registrar_port}"); - - #[cfg(not(test))] - let addr = format!( - "http://{remote_ip}:{registrar_port}/{API_VERSION}/agents/{agent_uuid}" - ); - - info!( - "Requesting agent activation from {} for {}", - addr, agent_uuid - ); - - let resp = reqwest::Client::new().put(&addr).json(&data).send().await?; - - if !resp.status().is_success() { - return Err(Error::Registrar { - addr, - code: resp.status().as_u16(), - }); - } - - let resp: Response = resp.json().await?; - - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -pub(crate) async fn do_register_agent( - registrar_ip: &str, - registrar_port: u32, - agent_uuid: &str, - ek_tpm: &[u8], - ekcert: Option>, - aik_tpm: &[u8], - iak_tpm: Option<&[u8]>, - idevid_tpm: Option<&[u8]>, - idevid_cert_x509: Option, - iak_cert_x509: Option, - iak_attest: Option>, - iak_sign: Option>, - mtls_cert_x509: Option<&X509>, - ip: &str, - port: u32, -) -> crate::error::Result> { - let mtls_cert = match mtls_cert_x509 { - Some(cert) => Some(crate::crypto::x509_to_pem(cert)?), - None => Some("disabled".to_string()), - }; - - let idevid_cert = match idevid_cert_x509 { - Some(cert) => Some(crate::crypto::x509_to_der(&cert)?), - None => None, - }; - - let iak_cert = match iak_cert_x509 { - Some(cert) => Some(crate::crypto::x509_to_der(&cert)?), - None => None, - }; - - let ip = if ip.is_empty() { - None - } else { - Some(ip.to_string()) - }; - - let data = Register { - ekcert, - ek_tpm, - aik_tpm, - iak_tpm, - idevid_tpm, - idevid_cert, - iak_cert, - iak_attest, - iak_sign, - mtls_cert, - ip, - port: Some(port), - }; - - let remote_ip = match registrar_ip.parse::() { - Ok(addr) => { - // Add brackets if the address is IPv6 - if addr.is_ipv6() { - format!("[{registrar_ip}]") - } else { - registrar_ip.to_string() - } - } - Err(_) => { - // The registrar_ip option can also be a hostname. If it is the case, the hostname was - // already validated during configuration - registrar_ip.to_string() - } - }; - - #[cfg(test)] - let addr = format!("http://{remote_ip}:{registrar_port}"); - - #[cfg(not(test))] - let addr = format!( - "http://{remote_ip}:{registrar_port}/{API_VERSION}/agents/{agent_uuid}" - ); - - info!( - "Requesting agent registration from {} for {}", - addr, agent_uuid - ); - - let resp = reqwest::Client::new() - .post(&addr) - .json(&data) - .send() - .await?; - - if !resp.status().is_success() { - return Err(Error::Registrar { - addr, - code: resp.status().as_u16(), - }); - } - - let resp: Response = resp.json().await?; - - if resp.results.blob.is_some() { - Ok(resp.results.blob.unwrap()) //#[allow_ci] - } else { - Ok(Vec::new()) - } -} - -#[cfg(feature = "testing")] -#[cfg(test)] -mod tests { - use super::*; - use keylime::crypto; - use wiremock::matchers::{any, method}; - use wiremock::{Mock, MockServer, ResponseTemplate}; - - #[actix_rt::test] - async fn mock_register_agent_ok() { - let response: Response = Response { - code: 200.into(), - status: "OK".to_string(), - results: RegisterResponseResults { blob: None }, - }; - - let mock_server = MockServer::start().await; - let mock = Mock::given(method("POST")) - .respond_with(ResponseTemplate::new(200).set_body_json(response)); - mock_server.register(mock).await; - - let uri = mock_server.uri(); - let uri = uri.split("//").collect::>()[1] - .split(':') - .collect::>(); - assert_eq!(uri.len(), 2); - - let addr = format!("http://{}:{}", uri[0], uri[1]); - - let ip = uri[0]; - let port = uri[1].parse().unwrap(); //#[allow_ci] - - let mock_data = [0u8; 1]; - let priv_key = crypto::testing::rsa_generate(2048).unwrap(); //#[allow_ci] - let cert = crypto::x509::CertificateBuilder::new() - .private_key(&priv_key) - .common_name("uuid") - .add_ips(vec!["1.2.3.4"]) - .build() - .unwrap(); //#[allow_ci] - let response = do_register_agent( - ip, - port, - "uuid", - &mock_data, - Some(mock_data.to_vec()), - &mock_data, - None, - None, - None, - None, - None, - None, - Some(&cert), - "1.2.3.4", - 0, - ) - .await; - assert!(response.is_ok()); - } - - #[actix_rt::test] - async fn mock_register_agent_ok_without_ekcert() { - let response: Response = Response { - code: 200.into(), - status: "OK".to_string(), - results: RegisterResponseResults { blob: None }, - }; - - let mock_server = MockServer::start().await; - let mock = Mock::given(method("POST")) - .respond_with(ResponseTemplate::new(200).set_body_json(response)); - mock_server.register(mock).await; - - let uri = mock_server.uri(); - let uri = uri.split("//").collect::>()[1] - .split(':') - .collect::>(); - assert_eq!(uri.len(), 2); - - let addr = format!("http://{}:{}", uri[0], uri[1]); - - let ip = uri[0]; - let port = uri[1].parse().unwrap(); //#[allow_ci] - - let mock_data = [0u8; 1]; - let priv_key = crypto::testing::rsa_generate(2048).unwrap(); //#[allow_ci] - let cert = crypto::x509::CertificateBuilder::new() - .private_key(&priv_key) - .common_name("uuid") - .add_ips(vec!["1.2.3.4", "1.2.3.5"]) - .build() - .unwrap(); //#[allow_ci] - let response = do_register_agent( - ip, - port, - "uuid", - &mock_data, - None, - &mock_data, - None, - None, - None, - None, - None, - None, - Some(&cert), - "", - 0, - ) - .await; - assert!(response.is_ok()); - } - - #[actix_rt::test] - async fn mock_register_agent_err() { - let response: Response = Response { - code: 200.into(), - status: "OK".to_string(), - results: RegisterResponseResults { blob: None }, - }; - - let mock_server = MockServer::start().await; - let uri = mock_server.uri(); - let uri = uri.split("//").collect::>()[1] - .split(':') - .collect::>(); - assert_eq!(uri.len(), 2); - - let addr = format!("http://{}:{}", uri[0], uri[1]); - - let ip = uri[0]; - let port = uri[1].parse().unwrap(); //#[allow_ci] - - let mock_data = [0u8; 1]; - let priv_key = crypto::testing::rsa_generate(2048).unwrap(); //#[allow_ci] - let cert = crypto::x509::CertificateBuilder::new() - .private_key(&priv_key) - .common_name("uuid") - .build() - .unwrap(); //#[allow_ci] - let response = do_register_agent( - ip, - port, - "uuid", - &mock_data, - Some(mock_data.to_vec()), - &mock_data, - None, - None, - None, - None, - None, - None, - Some(&cert), - "", - 0, - ) - .await; - assert!(response.is_err()); - assert_eq!(response.err().unwrap().http_code().unwrap(), 404); //#[allow_ci] - } - - #[actix_rt::test] - async fn mock_activate_agent_ok() { - let response: Response = Response { - code: 200.into(), - status: "OK".to_string(), - results: ActivateResponseResults {}, - }; - - let mock_server = MockServer::start().await; - let mock = Mock::given(method("PUT")) - .respond_with(ResponseTemplate::new(200).set_body_json(response)); - mock_server.register(mock).await; - - let uri = mock_server.uri(); - let uri = uri.split("//").collect::>()[1] - .split(':') - .collect::>(); - assert_eq!(uri.len(), 2); - - let addr = format!("http://{}:{}", uri[0], uri[1]); - - let ip = uri[0]; - let port = uri[1].parse().unwrap(); //#[allow_ci] - - let response = do_activate_agent(ip, port, "uuid", "tag").await; - assert!(response.is_ok()); - } - - #[actix_rt::test] - async fn mock_activate_agent_err() { - let response: Response = Response { - code: 200.into(), - status: "OK".to_string(), - results: ActivateResponseResults {}, - }; - - let mock_server = MockServer::start().await; - let uri = mock_server.uri(); - let uri = uri.split("//").collect::>()[1] - .split(':') - .collect::>(); - assert_eq!(uri.len(), 2); - - let addr = format!("http://{}:{}", uri[0], uri[1]); - - let ip = uri[0]; - let port = uri[1].parse().unwrap(); //#[allow_ci] - - let response = do_activate_agent(ip, port, "uuid", "tag").await; - assert!(response.is_err()); - assert_eq!(response.err().unwrap().http_code().unwrap(), 404); //#[allow_ci] - } -}