From b6035afe2c75f73251576fda11d4a5eec4438587 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 29 Jan 2024 13:55:16 +0800 Subject: [PATCH] AA/attester: add check_init_data support for TDX and SNP In TDX, we use tdx_attest crate to get a raw hardware tdx report to parse the MRCONFIGID field. In SNP, we use sev crate to get a hardware report to parse HOSTDATA field. The input one should be resize as the evidence field inside the TEE evidence to compare. Signed-off-by: Magnus Kulke Signed-off-by: Dan Mihai Signed-off-by: Xynnn007 --- Cargo.lock | 22 +++++ attestation-agent/attester/Cargo.toml | 4 +- attestation-agent/attester/src/lib.rs | 1 + .../attester/src/snp/hostdata.rs | 24 +++++ attestation-agent/attester/src/snp/mod.rs | 14 +++ attestation-agent/attester/src/tdx/mod.rs | 35 ++++++- attestation-agent/attester/src/tdx/report.rs | 99 +++++++++++++++++++ attestation-agent/attester/src/utils.rs | 15 +++ 8 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 attestation-agent/attester/src/snp/hostdata.rs create mode 100644 attestation-agent/attester/src/tdx/report.rs create mode 100644 attestation-agent/attester/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 9045329e3..b399baad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,11 +287,13 @@ dependencies = [ "log", "nix 0.26.4", "occlum_dcap", + "scroll", "serde", "serde_json", "sev 1.2.1", "strum", "tdx-attest-rs", + "thiserror", "tokio", ] @@ -4806,6 +4808,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + [[package]] name = "scrypt" version = "0.11.0" diff --git a/attestation-agent/attester/Cargo.toml b/attestation-agent/attester/Cargo.toml index 6dd1e98db..e194e8e1d 100644 --- a/attestation-agent/attester/Cargo.toml +++ b/attestation-agent/attester/Cargo.toml @@ -19,6 +19,7 @@ kbs-types.workspace = true log.workspace = true nix = { version = "0.26.2", optional = true } occlum_dcap = { git = "https://github.com/occlum/occlum", tag = "v0.29.7", optional = true } +scroll = { version = "0.11.0", default-features = false, features = ["derive"], optional = true } serde.workspace = true serde_json.workspace = true sev = { version = "1.2.0", default-features = false, features = [ @@ -26,6 +27,7 @@ sev = { version = "1.2.0", default-features = false, features = [ ], optional = true } strum.workspace = true tdx-attest-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true } +thiserror.workspace = true # TODO: change it to "0.1", once released. csv-rs = { git = "https://github.com/openanolis/csv-rs", rev = "b74aa8c", optional = true } codicon = { version = "3.0", optional = true } @@ -52,7 +54,7 @@ all-attesters = [ "cca-attester", ] -tdx-attester = ["tdx-attest-rs"] +tdx-attester = ["scroll", "tdx-attest-rs"] sgx-attester = ["occlum_dcap"] az-snp-vtpm-attester = ["az-snp-vtpm"] az-tdx-vtpm-attester = ["az-tdx-vtpm"] diff --git a/attestation-agent/attester/src/lib.rs b/attestation-agent/attester/src/lib.rs index 0d47a678d..41e8b2368 100644 --- a/attestation-agent/attester/src/lib.rs +++ b/attestation-agent/attester/src/lib.rs @@ -7,6 +7,7 @@ use anyhow::*; use kbs_types::Tee; pub mod sample; +pub mod utils; #[cfg(feature = "az-snp-vtpm-attester")] pub mod az_snp_vtpm; diff --git a/attestation-agent/attester/src/snp/hostdata.rs b/attestation-agent/attester/src/snp/hostdata.rs new file mode 100644 index 000000000..4dce51ce2 --- /dev/null +++ b/attestation-agent/attester/src/snp/hostdata.rs @@ -0,0 +1,24 @@ +// Copyright (c) 2024 Microsoft Corporation +// Copyright (c) 2024 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum GetHostDataError { + #[error("Open Sev guest firmware failed: {0}")] + OpenSevGuestFirmware(#[from] std::io::Error), + + #[error("Get report failed: {0}")] + GetReportError(#[from] sev::error::UserApiError), +} + +pub fn get_snp_host_data() -> Result<[u8; 32], GetHostDataError> { + let mut firmware = sev::firmware::guest::Firmware::open()?; + let report_data: [u8; 64] = [0; 64]; + let report = firmware.get_report(None, Some(report_data), Some(0))?; + + Ok(report.host_data) +} diff --git a/attestation-agent/attester/src/snp/mod.rs b/attestation-agent/attester/src/snp/mod.rs index ba950517c..743977f4e 100644 --- a/attestation-agent/attester/src/snp/mod.rs +++ b/attestation-agent/attester/src/snp/mod.rs @@ -3,6 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // +use crate::utils::pad; + use super::Attester; use anyhow::*; use serde::{Deserialize, Serialize}; @@ -11,6 +13,8 @@ use sev::firmware::guest::Firmware; use sev::firmware::host::CertTableEntry; use std::path::Path; +mod hostdata; + pub fn detect_platform() -> bool { Path::new("/sys/devices/platform/sev-guest").exists() } @@ -47,4 +51,14 @@ impl Attester for SnpAttester { serde_json::to_string(&evidence).context("Serialize SNP evidence failed") } + + async fn check_init_data(&self, init_data: &[u8]) -> Result<()> { + let hostdata = hostdata::get_snp_host_data().context("Get HOSTDATA failed")?; + let init_data: [u8; 32] = pad(init_data); + if init_data != hostdata { + bail!("HOSTDATA does not match."); + } + + Ok(()) + } } diff --git a/attestation-agent/attester/src/tdx/mod.rs b/attestation-agent/attester/src/tdx/mod.rs index aa0d1cf15..78aa619d2 100644 --- a/attestation-agent/attester/src/tdx/mod.rs +++ b/attestation-agent/attester/src/tdx/mod.rs @@ -3,12 +3,18 @@ // SPDX-License-Identifier: Apache-2.0 // +use crate::utils::pad; + use super::Attester; use anyhow::*; use base64::Engine; +use log::debug; +use scroll::Pread; use serde::{Deserialize, Serialize}; use std::path::Path; -use tdx_attest_rs; +use tdx_attest_rs::{self, tdx_report_t}; + +mod report; const TDX_REPORT_DATA_SIZE: usize = 64; const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; @@ -90,6 +96,33 @@ impl Attester for TdxAttester { Ok(()) } + + async fn check_init_data(&self, init_data: &[u8]) -> Result<()> { + let mut report = tdx_report_t { d: [0; 1024] }; + match tdx_attest_rs::tdx_att_get_report(None, &mut report) { + tdx_attest_rs::tdx_attest_error_t::TDX_ATTEST_SUCCESS => { + debug!("Successfully get report") + } + error_code => { + bail!( + "TDX Attester: Failed to get TD report. Error code: {:?}", + error_code + ); + } + }; + + let td_report = report + .d + .pread::(0) + .map_err(|e| anyhow!("Parse TD report failed: {:?}", e))?; + + let init_data: [u8; 48] = pad(init_data); + if init_data != td_report.tdinfo.mrconfigid { + bail!("Init data does not match!"); + } + + Ok(()) + } } #[cfg(test)] diff --git a/attestation-agent/attester/src/tdx/report.rs b/attestation-agent/attester/src/tdx/report.rs new file mode 100644 index 000000000..5c7a39c52 --- /dev/null +++ b/attestation-agent/attester/src/tdx/report.rs @@ -0,0 +1,99 @@ +// Copyright (c) 2024 Microsoft Corporation +// Copyright (c) 2024 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +use scroll::Pread; + +#[repr(C)] +#[derive(Pread)] +/// Type header of TDREPORT_STRUCT. +pub struct TdTransportType { + /// Type of the TDREPORT (0 - SGX, 81 - TDX, rest are reserved). + pub type_: u8, + + /// Subtype of the TDREPORT (Default value is 0). + pub sub_type: u8, + + /// TDREPORT version (Default value is 0). + pub version: u8, + + /// Added for future extension. + pub reserved: u8, +} + +#[repr(C)] +#[derive(Pread)] +/// TDX guest report data, MAC and TEE hashes. +pub struct ReportMac { + /// TDREPORT type header. + pub type_: TdTransportType, + + /// Reserved for future extension. + pub reserved1: [u8; 12], + + /// CPU security version. + pub cpu_svn: [u8; 16], + + /// SHA384 hash of TEE TCB INFO. + pub tee_tcb_info_hash: [u8; 48], + + /// SHA384 hash of TDINFO_STRUCT. + pub tee_td_info_hash: [u8; 48], + + /// User defined unique data passed in TDG.MR.REPORT request. + pub reportdata: [u8; 64], + + /// Reserved for future extension. + pub reserved2: [u8; 32], + + /// CPU MAC ID. + pub mac: [u8; 32], +} + +#[repr(C)] +#[derive(Pread)] +/// TDX guest measurements and configuration. +pub struct TdInfo { + /// TDX Guest attributes (like debug, spet_disable, etc). + pub attr: [u8; 8], + + /// Extended features allowed mask. + pub xfam: u64, + + /// Build time measurement register. + pub mrtd: [u64; 6], + + /// Software-defined ID for non-owner-defined configuration of the guest - e.g., run-time or OS configuration. + pub mrconfigid: [u8; 48], + + /// Software-defined ID for the guest owner. + pub mrowner: [u64; 6], + + /// Software-defined ID for owner-defined configuration of the guest - e.g., specific to the workload. + pub mrownerconfig: [u64; 6], + + /// Run time measurement registers. + pub rtmr: [u64; 24], + + /// For future extension. + pub reserved: [u64; 14], +} + +#[repr(C)] +#[derive(Pread)] +/// Output of TDCALL[TDG.MR.REPORT]. +pub struct TdReport { + /// Mac protected header of size 256 bytes. + pub report_mac: ReportMac, + + /// Additional attestable elements in the TCB are not reflected in the report_mac. + pub tee_tcb_info: [u8; 239], + + /// Added for future extension. + pub reserved: [u8; 17], + + /// Measurements and configuration data of size 512 bytes. + pub tdinfo: TdInfo, +} diff --git a/attestation-agent/attester/src/utils.rs b/attestation-agent/attester/src/utils.rs new file mode 100644 index 000000000..0f7da9ea6 --- /dev/null +++ b/attestation-agent/attester/src/utils.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2024 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +pub fn pad(input: &[u8]) -> [u8; T] { + let mut output = [0; T]; + let len = input.len(); + if len > T { + output.copy_from_slice(&input[..T]); + } else { + output[..len].copy_from_slice(input); + } + output +}