From f26388bb20b664207a1a398fb867c893b01658e0 Mon Sep 17 00:00:00 2001 From: Conrad Grobler Date: Fri, 10 Jan 2025 11:21:34 +0000 Subject: [PATCH] Make Orchestrator generic with the Attester This required creating a temporary (already deprecated) trait to support clients that still expect a separate `application_keys` field in the evidence. It might take many months for all client installations to be updated, so this trait is needed to unblock the Intel TDX attestation. BUG: 380442628 Change-Id: If87d01ec893472a7b0970d925a4327aff29836cd --- oak_attestation/src/dice.rs | 59 +++------------------ oak_attestation/src/lib.rs | 27 ++++++++++ oak_containers/orchestrator/BUILD | 11 +++- oak_containers/orchestrator/Cargo.toml | 6 +++ oak_containers/orchestrator/src/dice.rs | 20 ++++--- oak_containers/orchestrator/src/lib.rs | 41 ++++++++------ oak_containers/orchestrator_bin/BUILD | 1 + oak_containers/orchestrator_bin/src/main.rs | 2 +- oak_containers_sdk/src/standalone.rs | 4 +- 9 files changed, 95 insertions(+), 76 deletions(-) diff --git a/oak_attestation/src/dice.rs b/oak_attestation/src/dice.rs index ed6c4a1a4b..6d5d3a2100 100644 --- a/oak_attestation/src/dice.rs +++ b/oak_attestation/src/dice.rs @@ -43,6 +43,9 @@ use prost::Message; use sha2::Digest; use zeroize::Zeroize; +#[allow(deprecated)] +use crate::ApplicationKeysAttester; + pub trait MeasureDigest { fn measure_digest(&self) -> RawDigest; } @@ -70,62 +73,16 @@ pub struct DiceAttester { signing_key: SigningKey, } -// TODO: b/366141836 - Remove this implementation once all Oak clients have been -// updated to the EventLog attestation. -impl DiceAttester { - /// Adds an additional layer of evidence to the DICE data. - /// - /// The evidence is in the form of a CWT certificate that contains the - /// `additional_claims` provided. Adding a layer generates a new ECA - /// private key for the layer and uses it to replace the existing - /// signing key. The CWT certificate contains the public key for this new - /// signing key. - pub fn add_layer(&mut self, layer_data: LayerData) -> anyhow::Result<()> { - // The last evidence layer contains the certificate for the current signing key. - // Since the builder contains an existing signing key there must be at - // least one layer of evidence that contains the certificate. - let layer_evidence = self - .evidence - .layers - .last() - .ok_or_else(|| anyhow::anyhow!("no evidence layers found"))?; - let claims_set = get_claims_set_from_certificate_bytes(&layer_evidence.eca_certificate) - .map_err(anyhow::Error::msg)?; - - // The issuer for the next layer is the subject of the current last layer. - let issuer_id = claims_set.subject.ok_or_else(|| anyhow!("no subject in certificate"))?; - - let evidence = &mut self.evidence; - let (signing_key, verifying_key) = generate_ecdsa_key_pair(); - - let eca_certificate = generate_signing_certificate( - &self.signing_key, - issuer_id, - &verifying_key, - layer_data.additional_claims, - ) - .map_err(anyhow::Error::msg) - .context("couldn't generate ECA certificate for the next layer")?; - evidence.layers.push(LayerEvidence { - eca_certificate: eca_certificate.to_vec().map_err(anyhow::Error::msg)?, - }); - // Replacing the signing key will cause the previous signing key to be dropped, - // which will zero out its memory. - self.signing_key = signing_key; - self.evidence - .event_log - .get_or_insert_with(EventLog::default) - .encoded_events - .push(layer_data.encoded_event); - Ok(()) - } - +// TODO: b/368030563 - Remove this implementation once all client library +// instances use the applications keys from the event log. +#[allow(deprecated)] +impl ApplicationKeysAttester for DiceAttester { /// Adds the CWT certificates application keys to the DICE data. /// /// Since no additional evidence can be added after the application keys are /// added, this consumes DICE data, discards the signing key and returns /// the finalized evidence. - pub fn add_application_keys( + fn add_application_keys( self, layer_data: LayerData, kem_public_key: &[u8], diff --git a/oak_attestation/src/lib.rs b/oak_attestation/src/lib.rs index eba0854eee..3a0a75890c 100644 --- a/oak_attestation/src/lib.rs +++ b/oak_attestation/src/lib.rs @@ -18,4 +18,31 @@ extern crate alloc; +use dice::LayerData; +use oak_proto_rust::oak::attestation::v1::Evidence; +use p256::ecdsa::VerifyingKey; + pub mod dice; + +/// Deprecated trait that allow for explicitly adding application keys to the +/// attestation evidence. +#[deprecated = "Use application keys from the event log."] +pub trait ApplicationKeysAttester { + // TODO: b/368030563 - Remove this trait once all client library instances use + // the applications keys from the event log. + + /// Adds certificates representing the application keys to the attestation + /// evidence. + // + /// This is an outdated approach that has been replaced by adding the + /// applications keys to the event log. It is only retained for + /// compatibility while some client deployments still expect this format. + fn add_application_keys( + self, + layer_data: LayerData, + kem_public_key: &[u8], + verifying_key: &VerifyingKey, + group_kem_public_key: Option<&[u8]>, + group_verifying_key: Option<&VerifyingKey>, + ) -> anyhow::Result; +} diff --git a/oak_containers/orchestrator/BUILD b/oak_containers/orchestrator/BUILD index 41e3f3c335..7cb138bea0 100644 --- a/oak_containers/orchestrator/BUILD +++ b/oak_containers/orchestrator/BUILD @@ -33,7 +33,11 @@ rust_library( "src/lib.rs", "src/logging.rs", ], - crate_features = ["bazel"], + # TODO: b/368030563 - Remove this feature once all client library instances use + # the applications keys from the event log. + crate_features = [ + "application_keys", + ], deps = [ "//oak_attestation", "//oak_attestation_types", @@ -77,7 +81,10 @@ rust_library( rust_test( name = "oak_containers_orchestrator_test", crate = ":oak_containers_orchestrator", - crate_features = ["bazel"], + crate_features = [ + "bazel", + "application_keys", + ], data = [ "//oak_containers/orchestrator/testdata:cdi.json", "//oak_containers/orchestrator/testdata:oci_spec_base.json", diff --git a/oak_containers/orchestrator/Cargo.toml b/oak_containers/orchestrator/Cargo.toml index 8049b2708d..75610d0bb9 100644 --- a/oak_containers/orchestrator/Cargo.toml +++ b/oak_containers/orchestrator/Cargo.toml @@ -8,6 +8,12 @@ authors = ["Juliette Pretot "] edition = "2021" license = "Apache-2.0" +[features] +# TODO: b/368030563 - Remove this feature once all client library instances use +# the applications keys from the event log. +application_keys = [] +default = ["application_keys"] + [dependencies] anyhow = "*" clap = { version = "*", features = ["derive", "env"] } diff --git a/oak_containers/orchestrator/src/dice.rs b/oak_containers/orchestrator/src/dice.rs index 141aac1633..ebd4b7ce98 100644 --- a/oak_containers/orchestrator/src/dice.rs +++ b/oak_containers/orchestrator/src/dice.rs @@ -19,8 +19,9 @@ use std::{ }; use anyhow::Context; -use oak_attestation::dice::DiceAttester; -use oak_attestation_types::util::Serializable; +#[allow(deprecated)] +use oak_attestation::ApplicationKeysAttester; +use oak_attestation_types::{attester::Attester, util::Serializable}; use zeroize::Zeroize; /// The path to the file where the DICE data provided by Stage 1 is stored. @@ -30,11 +31,16 @@ const STAGE1_DICE_DATA_PATH: &str = "/oak/dice"; /// /// The file is also overwritten with zeros to ensure it cannot be reused by /// another process. -pub fn load_stage1_dice_data() -> anyhow::Result { +#[allow(deprecated)] +pub fn load_stage1_dice_data( +) -> anyhow::Result { load_stage1_dice_data_from_path(STAGE1_DICE_DATA_PATH) } -fn load_stage1_dice_data_from_path(path: &str) -> anyhow::Result { +#[allow(deprecated)] +fn load_stage1_dice_data_from_path( + path: &str, +) -> anyhow::Result { let mut file = OpenOptions::new() .read(true) .write(true) @@ -45,7 +51,7 @@ fn load_stage1_dice_data_from_path(path: &str) -> anyhow::Result { let mut buffer = Vec::with_capacity(size); file.read_to_end(&mut buffer).context("couldn't read DICE data from file")?; - let result = DiceAttester::deserialize(&buffer[..]).context("couldn't parse DICE data")?; + let result = A::deserialize(&buffer[..]).context("couldn't parse DICE data")?; buffer.zeroize(); file.rewind()?; @@ -64,6 +70,8 @@ fn load_stage1_dice_data_from_path(path: &str) -> anyhow::Result { mod tests { use std::fs; + use oak_attestation::dice::DiceAttester; + use super::*; #[test] @@ -76,7 +84,7 @@ mod tests { #[cfg(not(feature = "bazel"))] fs::copy("testdata/test_dice", DICE_DATA_PATH).unwrap(); - load_stage1_dice_data_from_path(DICE_DATA_PATH).unwrap(); + load_stage1_dice_data_from_path::(DICE_DATA_PATH).unwrap(); let mut file = OpenOptions::new().read(true).open(DICE_DATA_PATH).unwrap(); let mut buffer = Vec::with_capacity(DICE_DATA_SIZE); file.read_to_end(&mut buffer).unwrap(); diff --git a/oak_containers/orchestrator/src/lib.rs b/oak_containers/orchestrator/src/lib.rs index d0718f5075..11805c3fa6 100644 --- a/oak_containers/orchestrator/src/lib.rs +++ b/oak_containers/orchestrator/src/lib.rs @@ -18,7 +18,9 @@ use std::{path::PathBuf, sync::Arc}; use anyhow::{anyhow, Context}; use clap::Parser; use launcher_client::LauncherClient; -use oak_attestation_types::attester::Attester; +#[allow(deprecated)] +use oak_attestation::ApplicationKeysAttester; +use oak_attestation_types::{attester::Attester, util::Serializable}; use oak_containers_agent::{metrics::MetricsConfig, set_error_handler}; use oak_containers_attestation::generate_instance_keys; use oak_proto_rust::oak::containers::v1::KeyProvisioningRole; @@ -51,7 +53,8 @@ struct Args { runtime_user: String, } -pub async fn main() -> anyhow::Result<()> { +#[allow(deprecated)] +pub async fn main() -> anyhow::Result<()> { crate::logging::setup()?; let args = Args::parse(); @@ -99,7 +102,7 @@ pub async fn main() -> anyhow::Result<()> { .map_err(|error| anyhow!("couldn't get application config: {:?}", error))?; // Create a container event and add it to the event log. - let mut attester = crate::dice::load_stage1_dice_data()?; + let mut attester: A = crate::dice::load_stage1_dice_data()?; let container_event = oak_containers_attestation::create_container_event( &container_bundle, &application_config, @@ -111,18 +114,26 @@ pub async fn main() -> anyhow::Result<()> { // Add the container event to the DICE chain. let container_layer = oak_containers_attestation::create_container_dice_layer(&container_event); - let evidence = attester.add_application_keys( - container_layer, - &instance_public_keys.encryption_public_key, - &instance_public_keys.signing_public_key, - if let Some(ref group_public_keys) = group_public_keys { - Some(&group_public_keys.encryption_public_key) - } else { - None - }, - None, - )?; - + let evidence = { + #[cfg(feature = "application_keys")] + { + attester.add_application_keys( + container_layer, + &instance_public_keys.encryption_public_key, + &instance_public_keys.signing_public_key, + if let Some(ref group_public_keys) = group_public_keys { + Some(&group_public_keys.encryption_public_key) + } else { + None + }, + None, + )? + } + #[cfg(not(feature = "application_keys"))] + { + attester.quote()? + } + }; // Send the attestation evidence to the Hostlib. launcher_client .send_attestation_evidence(evidence.clone()) diff --git a/oak_containers/orchestrator_bin/BUILD b/oak_containers/orchestrator_bin/BUILD index b6c77e4fa7..7c05e60551 100644 --- a/oak_containers/orchestrator_bin/BUILD +++ b/oak_containers/orchestrator_bin/BUILD @@ -25,6 +25,7 @@ rust_binary( name = "bin/oak_containers_orchestrator", srcs = ["src/main.rs"], deps = [ + "//oak_attestation", "//oak_containers/orchestrator", "@oak_crates_index//:anyhow", "@oak_crates_index//:tikv-jemallocator", diff --git a/oak_containers/orchestrator_bin/src/main.rs b/oak_containers/orchestrator_bin/src/main.rs index 5cc522671b..8a89801329 100644 --- a/oak_containers/orchestrator_bin/src/main.rs +++ b/oak_containers/orchestrator_bin/src/main.rs @@ -18,5 +18,5 @@ static ALLOCATOR: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[tokio::main] async fn main() -> anyhow::Result<()> { - oak_containers_orchestrator::main().await + oak_containers_orchestrator::main::().await } diff --git a/oak_containers_sdk/src/standalone.rs b/oak_containers_sdk/src/standalone.rs index 8ecdc9f58c..9c4a758f06 100644 --- a/oak_containers_sdk/src/standalone.rs +++ b/oak_containers_sdk/src/standalone.rs @@ -24,7 +24,8 @@ use anyhow::{Context, Result}; use async_trait::async_trait; -use oak_attestation::dice::DiceAttester; +#[allow(deprecated)] +use oak_attestation::{dice::DiceAttester, ApplicationKeysAttester}; use oak_attestation_types::attester::Attester; use oak_containers_attestation::{InstanceKeys, InstancePublicKeys}; use oak_crypto::{ @@ -196,6 +197,7 @@ impl StandaloneOrchestrator { oak_containers_attestation::create_container_dice_layer(&container_event); // Add application keys and generate the final evidence. + #[allow(deprecated)] let evidence = attester.add_application_keys( container_layer, &instance_public_keys.encryption_public_key,