diff --git a/Cargo.lock b/Cargo.lock index 990e52628..ed8811faa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,7 @@ dependencies = [ "strum", "tdx-attest-rs", "tokio", + "vmm-sys-util", ] [[package]] diff --git a/attestation-agent/attester/Cargo.toml b/attestation-agent/attester/Cargo.toml index 6dd1e98db..19bee0c1e 100644 --- a/attestation-agent/attester/Cargo.toml +++ b/attestation-agent/attester/Cargo.toml @@ -32,6 +32,7 @@ codicon = { version = "3.0", optional = true } hyper = { version = "0.14", features = ["full"], optional = true } hyper-tls = { version = "0.5", optional = true } tokio = { version = "1", features = ["full"], optional = true } +vmm-sys-util = { version = "0.11.0", optional = true } [dev-dependencies] tokio.workspace = true @@ -52,7 +53,7 @@ all-attesters = [ "cca-attester", ] -tdx-attester = ["tdx-attest-rs"] +tdx-attester = ["tdx-attest-rs", "vmm-sys-util"] 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/snp/hostdata.rs b/attestation-agent/attester/src/snp/hostdata.rs new file mode 100644 index 000000000..007f772dc --- /dev/null +++ b/attestation-agent/attester/src/snp/hostdata.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2024 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::Result; + +pub fn get_snp_host_data() -> Result> { + match sev::firmware::guest::Firmware::open() { + Ok(mut firmware) => { + let report_data: [u8; 64] = [0; 64]; + match firmware.get_report(None, Some(report_data), Some(0)) { + Ok(report) => Ok(report.host_data.to_vec()), + Err(e) => Err(e.into()), + } + } + Err(e) => Err(e.into()), + } +} diff --git a/attestation-agent/attester/src/snp/mod.rs b/attestation-agent/attester/src/snp/mod.rs index ba950517c..770663dfd 100644 --- a/attestation-agent/attester/src/snp/mod.rs +++ b/attestation-agent/attester/src/snp/mod.rs @@ -11,6 +11,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 +49,14 @@ impl Attester for SnpAttester { serde_json::to_string(&evidence).context("Serialize SNP evidence failed") } + + async fn check_init_data(&mut self, mut init_data: Vec) -> Result<()> { + let hostdata = hostdata::get_snp_host_data().context("Get HOSTDATA failed")?; + init_data.resize(core::mem::size_of::<[u8; 32]>(), b'0'); + 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..3df89e424 100644 --- a/attestation-agent/attester/src/tdx/mod.rs +++ b/attestation-agent/attester/src/tdx/mod.rs @@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize}; use std::path::Path; use tdx_attest_rs; +mod mrconfigid; + const TDX_REPORT_DATA_SIZE: usize = 64; const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; @@ -90,6 +92,16 @@ impl Attester for TdxAttester { Ok(()) } + + async fn check_init_data(&mut self, mut init_data: Vec) -> Result<()> { + let mr_config_id = mrconfigid::get_tdx_mrconfigid().context("Get MRCONFIGID failed")?; + init_data.resize(core::mem::size_of::<[u64; 6]>(), b'0'); + if init_data != mr_config_id { + bail!("MRCONFIGID does not match."); + } + + Ok(()) + } } #[cfg(test)] diff --git a/attestation-agent/attester/src/tdx/mrconfigid.rs b/attestation-agent/attester/src/tdx/mrconfigid.rs new file mode 100644 index 000000000..4b4872508 --- /dev/null +++ b/attestation-agent/attester/src/tdx/mrconfigid.rs @@ -0,0 +1,185 @@ +// Copyright (c) 2024 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{bail, Result}; +use nix::fcntl::{self, OFlag}; +use nix::sys::stat::Mode; +use std::os::fd::{AsRawFd, FromRawFd}; +use vmm_sys_util::ioctl::ioctl_with_val; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iowr_nr}; + +#[repr(C)] +#[derive(Default)] +/// Type header of TDREPORT_STRUCT. +struct TdTransportType { + /// Type of the TDREPORT (0 - SGX, 81 - TDX, rest are reserved). + type_: u8, + + /// Subtype of the TDREPORT (Default value is 0). + sub_type: u8, + + /// TDREPORT version (Default value is 0). + version: u8, + + /// Added for future extension. + reserved: u8, +} + +#[repr(C)] +/// TDX guest report data, MAC and TEE hashes. +struct ReportMac { + /// TDREPORT type header. + type_: TdTransportType, + + /// Reserved for future extension. + reserved1: [u8; 12], + + /// CPU security version. + cpu_svn: [u8; 16], + + /// SHA384 hash of TEE TCB INFO. + tee_tcb_info_hash: [u8; 48], + + /// SHA384 hash of TDINFO_STRUCT. + tee_td_info_hash: [u8; 48], + + /// User defined unique data passed in TDG.MR.REPORT request. + reportdata: [u8; 64], + + /// Reserved for future extension. + reserved2: [u8; 32], + + /// CPU MAC ID. + mac: [u8; 32], +} + +impl Default for ReportMac { + fn default() -> Self { + Self { + type_: Default::default(), + reserved1: [0; 12], + cpu_svn: [0; 16], + tee_tcb_info_hash: [0; 48], + tee_td_info_hash: [0; 48], + reportdata: [0; 64], + reserved2: [0; 32], + mac: [0; 32], + } + } +} + +#[repr(C)] +#[derive(Default)] +/// TDX guest measurements and configuration. +struct TdInfo { + /// TDX Guest attributes (like debug, spet_disable, etc). + attr: [u8; 8], + + /// Extended features allowed mask. + xfam: u64, + + /// Build time measurement register. + mrtd: [u64; 6], + + /// Software-defined ID for non-owner-defined configuration of the guest - e.g., run-time or OS configuration. + mrconfigid: [u64; 6], + + /// Software-defined ID for the guest owner. + mrowner: [u64; 6], + + /// Software-defined ID for owner-defined configuration of the guest - e.g., specific to the workload. + mrownerconfig: [u64; 6], + + /// Run time measurement registers. + rtmr: [u64; 24], + + /// For future extension. + reserved: [u64; 14], +} + +#[repr(C)] +/// Output of TDCALL[TDG.MR.REPORT]. +struct TdReport { + /// Mac protected header of size 256 bytes. + report_mac: ReportMac, + + /// Additional attestable elements in the TCB are not reflected in the report_mac. + tee_tcb_info: [u8; 239], + + /// Added for future extension. + reserved: [u8; 17], + + /// Measurements and configuration data of size 512 bytes. + tdinfo: TdInfo, +} + +impl Default for TdReport { + fn default() -> Self { + Self { + report_mac: Default::default(), + tee_tcb_info: [0; 239], + reserved: [0; 17], + tdinfo: Default::default(), + } + } +} + +#[repr(C)] +/// Request struct for TDX_CMD_GET_REPORT0 IOCTL. +struct TdxReportReq { + /// User buffer with REPORTDATA to be included into TDREPORT. + /// Typically it can be some nonce provided by attestation, service, + /// so the generated TDREPORT can be uniquely verified. + reportdata: [u8; 64], + + /// User buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT]. + tdreport: TdReport, +} + +// Get TDREPORT0 (a.k.a. TDREPORT subtype 0) using TDCALL[TDG.MR.REPORT]. +ioctl_iowr_nr!( + TDX_CMD_GET_REPORT0, + 'T' as ::std::os::raw::c_uint, + 1, + TdxReportReq +); + +pub fn get_tdx_mrconfigid() -> Result> { + let fd = { + let raw_fd = fcntl::open( + "/dev/tdx_guest", + OFlag::O_CLOEXEC | OFlag::O_RDWR | OFlag::O_SYNC, + Mode::empty(), + )?; + unsafe { std::fs::File::from_raw_fd(raw_fd) } + }; + + let mut req = TdxReportReq { + reportdata: [0; 64], + tdreport: Default::default(), + }; + let ret = unsafe { + ioctl_with_val( + &fd.as_raw_fd(), + TDX_CMD_GET_REPORT0(), + &mut req as *mut TdxReportReq as std::os::raw::c_ulong, + ) + }; + if ret < 0 { + bail!( + "TDX_CMD_GET_REPORT0 failed: {:?}", + std::io::Error::last_os_error(), + ); + } + + let mrconfigid: Vec = req + .tdreport + .tdinfo + .mrconfigid + .iter() + .flat_map(|val| val.to_le_bytes()) + .collect(); + Ok(mrconfigid) +}