Skip to content

Commit

Permalink
AA/attester: add check_init_data support for TDX and SNP
Browse files Browse the repository at this point in the history
In TDX, we use ioctl 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: Dan Mihai <[email protected]>
Signed-off-by: Xynnn007 <[email protected]>
  • Loading branch information
Xynnn007 committed Jan 29, 2024
1 parent 56a8067 commit c63fbe3
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion attestation-agent/attester/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"]
Expand Down
19 changes: 19 additions & 0 deletions attestation-agent/attester/src/snp/hostdata.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<u8>> {
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()),
}
}
12 changes: 12 additions & 0 deletions attestation-agent/attester/src/snp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down Expand Up @@ -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<u8>) -> 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(())
}
}
12 changes: 12 additions & 0 deletions attestation-agent/attester/src/tdx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -90,6 +92,16 @@ impl Attester for TdxAttester {

Ok(())
}

async fn check_init_data(&mut self, mut init_data: Vec<u8>) -> 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)]
Expand Down
185 changes: 185 additions & 0 deletions attestation-agent/attester/src/tdx/mrconfigid.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<u8>> {
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<u8> = req
.tdreport
.tdinfo
.mrconfigid
.iter()
.flat_map(|val| val.to_le_bytes())
.collect();
Ok(mrconfigid)
}

0 comments on commit c63fbe3

Please sign in to comment.