Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 59 additions & 5 deletions openhcl/tee_call/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ pub const HW_DERIVED_KEY_LENGTH: usize = x86defs::snp::SNP_DERIVED_KEY_SIZE;
// DEVNOTE: This value should be upper bound among all the supported TEE types.
pub const REPORT_DATA_SIZE: usize = x86defs::snp::SNP_REPORT_DATA_SIZE;

/// Size of `TVM_REPORT_V1`.
pub const TVM_REPORT_SIZE: usize = 396;

Comment on lines +43 to +45
// TDX and SNP report data size are equal so we can use either of them
static_assertions::const_assert_eq!(
x86defs::snp::SNP_REPORT_DATA_SIZE,
x86defs::tdx::TDX_REPORT_DATA_SIZE
);
static_assertions::const_assert!(TVM_REPORT_SIZE <= hvdef::hypercall::VBS_VM_MAX_REPORT_SIZE);

/// Type of the TEE
#[derive(Debug)]
Expand All @@ -57,6 +61,8 @@ pub enum TeeType {
Cca,
/// Virtualization-based Security (VBS)
Vbs,
/// Trusted Launch host certification
Host,
}

/// The result of the `get_attestation_report`.
Expand Down Expand Up @@ -209,16 +215,22 @@ impl TeeCall for TdxCall {
/// Implementation of [`TeeCall`] for VBS
pub struct VbsCall;

fn get_vbs_vm_report(
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<[u8; hvdef::hypercall::VBS_VM_MAX_REPORT_SIZE], Error> {
let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
mshv_hvcall
.vbs_vm_call_report(report_data)
.map_err(Error::GetVbsReport)
}

impl TeeCall for VbsCall {
fn get_attestation_report(
&self,
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<GetAttestationReportResult, Error> {
let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
let report = mshv_hvcall
.vbs_vm_call_report(report_data)
.map_err(Error::GetVbsReport)?;
let report = get_vbs_vm_report(report_data)?;

Ok(GetAttestationReportResult {
report: report[..hvdef::vbs::VBS_REPORT_SIZE].to_vec(),
Expand All @@ -237,3 +249,45 @@ impl TeeCall for VbsCall {
TeeType::Vbs
}
}

/// Implementation of [`TeeCall`] for Trusted Launch host certification.
pub struct HostCall;

impl TeeCall for HostCall {
fn get_attestation_report(
&self,
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<GetAttestationReportResult, Error> {
let report = get_vbs_vm_report(report_data)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this additional API if it does the same as VBS?


Ok(GetAttestationReportResult {
report: report[..TVM_REPORT_SIZE].to_vec(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, currently TVM_REPORT_SIZE is 0

tcb_version: None,
})
}

fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
None
}

fn tee_type(&self) -> TeeType {
TeeType::Host
}
}

#[cfg(test)]
mod tests {
use super::HostCall;
use super::TeeCall;
use super::TeeType;

#[test]
fn host_call_tee_type_is_host() {
assert!(matches!(HostCall.tee_type(), TeeType::Host));
}

#[test]
fn host_call_does_not_support_derived_key() {
assert!(HostCall.supports_get_derived_key().is_none());
}
}
202 changes: 201 additions & 1 deletion openhcl/underhill_attestation/src/igvm_attest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ pub enum ReportType {
Cca,
/// Trusted VM report
Tvm,
/// Trusted VM report signed by the host IDK_S key
TvmHostCertified,
}

impl ReportType {
Expand All @@ -86,7 +88,7 @@ impl ReportType {
Self::Snp => IgvmAttestReportType::SNP_VM_REPORT,
Self::Tdx => IgvmAttestReportType::TDX_VM_REPORT,
Self::Cca => IgvmAttestReportType::CCA_VM_REPORT,
Self::Tvm => IgvmAttestReportType::TVM_REPORT,
Self::Tvm | Self::TvmHostCertified => IgvmAttestReportType::TVM_REPORT,
}
}
}
Expand Down Expand Up @@ -120,6 +122,7 @@ impl IgvmAttestRequestHelper {
TeeType::Tdx => ReportType::Tdx,
TeeType::Cca => ReportType::Cca,
TeeType::Vbs => ReportType::Vbs,
TeeType::Host => ReportType::TvmHostCertified,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a Host TeeType is a bit confusing

};

let attestation_vm_config =
Expand Down Expand Up @@ -157,6 +160,7 @@ impl IgvmAttestRequestHelper {
Some(TeeType::Tdx) => ReportType::Tdx,
Some(TeeType::Cca) => ReportType::Cca,
Some(TeeType::Vbs) => ReportType::Vbs,
Some(TeeType::Host) => ReportType::TvmHostCertified,
None => ReportType::Tvm,
};

Expand Down Expand Up @@ -335,6 +339,7 @@ fn get_report_size(report_type: &ReportType) -> usize {
ReportType::Snp => openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE,
ReportType::Tdx => openhcl_attestation_protocol::igvm_attest::get::TDX_VM_REPORT_SIZE,
ReportType::Tvm => openhcl_attestation_protocol::igvm_attest::get::TVM_REPORT_SIZE,
ReportType::TvmHostCertified => tee_call::TVM_REPORT_SIZE,
ReportType::Cca => todo!(),
}
}
Expand Down Expand Up @@ -387,6 +392,201 @@ mod tests {
assert!(result.is_err());
}

#[test]
fn test_create_tvm_requests() {
use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;

let legacy = create_request(
IGVM_ATTEST_REQUEST_CURRENT_VERSION,
IgvmAttestRequestType::AK_CERT_REQUEST,
&[],
&[],
&ReportType::Tvm,
IgvmAttestHashType::SHA_256,
);
assert!(legacy.is_ok());

let host_certified = create_request(
IGVM_ATTEST_REQUEST_CURRENT_VERSION,
IgvmAttestRequestType::AK_CERT_REQUEST,
&[],
&[0u8; tee_call::TVM_REPORT_SIZE],
&ReportType::TvmHostCertified,
IgvmAttestHashType::SHA_256,
);
assert!(host_certified.is_ok());

let missing_report = create_request(
IGVM_ATTEST_REQUEST_CURRENT_VERSION,
IgvmAttestRequestType::AK_CERT_REQUEST,
&[],
&[],
&ReportType::TvmHostCertified,
IgvmAttestHashType::SHA_256,
);
assert!(missing_report.is_err());
}

#[test]
fn test_tvm_host_certified_report_size_boundary_rejected() {
use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;

let too_small = create_request(
IGVM_ATTEST_REQUEST_CURRENT_VERSION,
IgvmAttestRequestType::AK_CERT_REQUEST,
&[],
&[0u8; tee_call::TVM_REPORT_SIZE - 1],
&ReportType::TvmHostCertified,
IgvmAttestHashType::SHA_256,
);
assert!(matches!(
too_small,
Err(Error::InvalidAttestationReportSize {
report_size,
expected_size,
}) if report_size == tee_call::TVM_REPORT_SIZE - 1
&& expected_size == tee_call::TVM_REPORT_SIZE
));

let too_large = create_request(
IGVM_ATTEST_REQUEST_CURRENT_VERSION,
IgvmAttestRequestType::AK_CERT_REQUEST,
&[],
&[0u8; tee_call::TVM_REPORT_SIZE + 1],
&ReportType::TvmHostCertified,
IgvmAttestHashType::SHA_256,
);
assert!(matches!(
too_large,
Err(Error::InvalidAttestationReportSize {
report_size,
expected_size,
}) if report_size == tee_call::TVM_REPORT_SIZE + 1
&& expected_size == tee_call::TVM_REPORT_SIZE
));
}

#[test]
fn test_tvm_host_certified_request_bytes() {
use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestVersion;

let runtime_claims = vec![7u8, 8, 9];
let attestation_report: Vec<u8> = (0..tee_call::TVM_REPORT_SIZE).map(|i| i as u8).collect();

let buffer = create_request(
IgvmAttestRequestVersion::VERSION_2,
IgvmAttestRequestType::AK_CERT_REQUEST,
&runtime_claims,
&attestation_report,
&ReportType::TvmHostCertified,
IgvmAttestHashType::SHA_256,
)
.expect("request generation");

let (request, _) =
IgvmAttestRequestBase::read_from_prefix(&buffer).expect("parse IgvmAttestRequest");

assert_eq!(
request.header.report_size,
(size_of::<IgvmAttestRequestBase>()
+ size_of::<openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt>(
)
+ runtime_claims.len()) as u32
);
assert_eq!(
&request.attestation_report[..tee_call::TVM_REPORT_SIZE],
attestation_report.as_slice()
);
assert!(
request.attestation_report[tee_call::TVM_REPORT_SIZE..]
.iter()
.all(|&b| b == 0)
);
assert_eq!(
request.request_data.report_type,
IgvmAttestReportType::TVM_REPORT
);
assert_eq!(
request.request_data.variable_data_size,
runtime_claims.len() as u32
);

let header_size = size_of::<IgvmAttestRequestBase>();
let extension_size =
size_of::<openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestDataExt>();
assert_eq!(
&buffer[header_size + extension_size..],
runtime_claims.as_slice()
);
}

#[test]
fn test_prepare_ak_cert_request_host_report_size() {
use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION;

let attestation_vm_config = AttestationVmConfig {
current_time: None,
root_cert_thumbprint: String::new(),
console_enabled: false,
interactive_console_enabled: false,
secure_boot: false,
tpm_enabled: false,
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::None,
filtered_vpci_devices_allowed: true,
vm_unique_id: String::new(),
vmgs_provisioner: None,
};

let host_report = [0u8; tee_call::TVM_REPORT_SIZE];

let host_helper = IgvmAttestRequestHelper::prepare_ak_cert_request(
Some(TeeType::Host),
&[],
&[],
&[],
&[],
&attestation_vm_config,
&[],
);
assert!(
host_helper
.create_request(IGVM_ATTEST_REQUEST_CURRENT_VERSION, &host_report)
.is_ok()
);

let legacy_helper = IgvmAttestRequestHelper::prepare_ak_cert_request(
None,
&[],
&[],
&[],
&[],
&attestation_vm_config,
&[],
);
assert!(
legacy_helper
.create_request(IGVM_ATTEST_REQUEST_CURRENT_VERSION, &host_report)
.is_err()
);

let vbs_helper = IgvmAttestRequestHelper::prepare_ak_cert_request(
Some(TeeType::Vbs),
&[],
&[],
&[],
&[],
&attestation_vm_config,
&[],
);
assert!(
vbs_helper
.create_request(IGVM_ATTEST_REQUEST_CURRENT_VERSION, &host_report)
.is_err()
);
}

#[test]
fn test_create_request_version1_no_extension() {
use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestBase;
Expand Down
Loading
Loading