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
61 changes: 33 additions & 28 deletions src/migtd/src/mig_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ mod v2 {
init_policy: &[u8],
init_event_log: &[u8],
init_td_report: &[u8],
servtd_ext_src: &[u8],
servtd_ext_src: Option<&[u8]>,
) -> Result<Vec<u8>, PolicyError> {
let policy_issuer_chain = get_policy_issuer_chain().ok_or(PolicyError::InvalidParameter)?;

Expand All @@ -284,34 +284,39 @@ mod v2 {
)?;
let policy = get_verified_policy().ok_or(PolicyError::InvalidParameter)?;

// Verify the td report init / event log init / policy init
let servtd_ext_src_obj =
ServtdExt::read_from_bytes(servtd_ext_src).ok_or(PolicyError::InvalidParameter)?;
let init_tdreport = verify_init_tdreport(init_td_report, &servtd_ext_src_obj)?;
let _engine_svn = policy
.servtd_tcb_mapping
.get_engine_svn_by_measurements(&Measurements::new_from_bytes(
&init_tdreport.td_info.mrtd,
&init_tdreport.td_info.rtmr0,
&init_tdreport.td_info.rtmr1,
None,
None,
))
.ok_or(PolicyError::SvnMismatch)?;
let verified_policy_init = verify_policy_and_event_log(
init_event_log,
init_policy,
policy_issuer_chain,
&get_rtmrs_from_tdreport(&init_tdreport)?,
)?;
// Verify the init TD report only when servtd_ext is available.
// Without servtd_ext (target TD ATTRIBUTES.SERVTDEXT=0), we cannot verify
// the init TD report's servtd_info_hash, so skip init report verification
// entirely and only rely on the current peer TD report verification above.
if let Some(ext_bytes) = servtd_ext_src {
let servtd_ext_src_obj =
ServtdExt::read_from_bytes(ext_bytes).ok_or(PolicyError::InvalidParameter)?;
let init_tdreport = verify_init_tdreport(init_td_report, &servtd_ext_src_obj)?;
let _engine_svn = policy
.servtd_tcb_mapping
.get_engine_svn_by_measurements(&Measurements::new_from_bytes(
&init_tdreport.td_info.mrtd,
&init_tdreport.td_info.rtmr0,
&init_tdreport.td_info.rtmr1,
None,
None,
))
.ok_or(PolicyError::SvnMismatch)?;
let verified_policy_init = verify_policy_and_event_log(
init_event_log,
init_policy,
policy_issuer_chain,
&get_rtmrs_from_tdreport(&init_tdreport)?,
)?;

let relative_reference =
get_init_tcb_evaluation_info(&init_tdreport, &verified_policy_init)?;
policy.policy_data.evaluate_policy_common(
&evaluation_data_src,
&relative_reference,
true,
)?;
let relative_reference =
get_init_tcb_evaluation_info(&init_tdreport, &verified_policy_init)?;
policy.policy_data.evaluate_policy_common(
&evaluation_data_src,
&relative_reference,
true,
)?;
}

// If backward policy exists, evaluate the migration src based on it.
let relative_reference = get_local_tcb_evaluation_info()?;
Expand Down
31 changes: 22 additions & 9 deletions src/migtd/src/migration/rebinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ async fn rebinding_old_prepare(
data: &mut Vec<u8>,
remote_policy: Vec<u8>,
) -> Result<(), MigrationResult> {
let servtd_ext = read_servtd_ext(info.binding_handle, &info.target_td_uuid)?;
let servtd_ext = read_servtd_ext(info.binding_handle, &info.target_td_uuid);
let init_policy_hash = digest_sha384(&init_migtd_data.init_policy)?;

// TLS client
Expand All @@ -614,7 +614,7 @@ async fn rebinding_old_prepare(
&init_policy_hash,
&init_migtd_data.init_report,
&init_migtd_data.init_event_log,
&servtd_ext,
servtd_ext.as_ref(),
)
.map_err(|_| {
#[cfg(feature = "vmcall-raw")]
Expand Down Expand Up @@ -677,8 +677,15 @@ async fn rebinding_new_prepare(
// The TLS session is established; we can now extract servtd_ext from the peer certificates.
let servtd_ext = get_servtd_ext_from_cert(&ratls_server.peer_certs())?;
write_rebinding_session_token(&rebind_token.token)?;
write_servtd_rebind_attr(&servtd_ext.cur_servtd_attr)?;
write_approved_servtd_ext_hash(&servtd_ext.calculate_approved_servtd_ext_hash()?)?;
if let Some(ext) = &servtd_ext {
write_servtd_rebind_attr(&ext.cur_servtd_attr)?;
}
write_approved_servtd_ext_hash(
servtd_ext
.map(|ext| ext.calculate_approved_servtd_ext_hash())
.transpose()?
.as_deref(),
)?;

shutdown_transport(ratls_server.transport_mut(), info.mig_request_id).await?;
Ok(())
Expand All @@ -689,7 +696,7 @@ async fn rebinding_new_finalize(
_data: &mut Vec<u8>,
) -> Result<(), MigrationResult> {
write_rebinding_session_token(&[0u8; 32])?;
write_approved_servtd_ext_hash(&[0u8; SHA384_DIGEST_SIZE])?;
write_approved_servtd_ext_hash(Some(&[0u8; SHA384_DIGEST_SIZE]))?;
Ok(())
}

Expand Down Expand Up @@ -733,7 +740,9 @@ pub fn approve_rebinding(
Ok(())
}

fn get_servtd_ext_from_cert(certs: &Option<Vec<&[u8]>>) -> Result<ServtdExt, MigrationResult> {
fn get_servtd_ext_from_cert(
certs: &Option<Vec<&[u8]>>,
) -> Result<Option<ServtdExt>, MigrationResult> {
if let Some(cert_chain) = certs {
if cert_chain.is_empty() {
return Err(MigrationResult::SecureSessionError);
Expand All @@ -748,10 +757,14 @@ fn get_servtd_ext_from_cert(certs: &Option<Vec<&[u8]>>) -> Result<ServtdExt, Mig
.as_ref()
.ok_or(MigrationResult::SecureSessionError)?;

let servtd_ext = find_extension(extensions, &EXTNID_MIGTD_SERVTD_EXT)
.ok_or(MigrationResult::SecureSessionError)?;
let servtd_ext = match find_extension(extensions, &EXTNID_MIGTD_SERVTD_EXT) {
Some(bytes) => bytes,
None => return Ok(None),
};

ServtdExt::read_from_bytes(servtd_ext).ok_or(MigrationResult::InvalidParameter)
ServtdExt::read_from_bytes(servtd_ext)
.ok_or(MigrationResult::InvalidParameter)
.map(Some)
} else {
Err(MigrationResult::SecureSessionError)
}
Expand Down
68 changes: 51 additions & 17 deletions src/migtd/src/migration/servtd_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ use tdx_tdcall::tdx::{tdcall_servtd_rd, tdcall_vm_write};

use crate::migration::MigrationResult;

/// Target TD’s ATTRIBUTES field in TDCS (readable via TDG.SERVTD.RD)
pub const TDCS_FIELD_ATTRIBUTES: u64 = 0x1110000300000000;
/// Bit 17 of ATTRIBUTES indicates SERVTD_EXT support
const ATTRIBUTES_SERVTDEXT_BIT: u64 = 1 << 17;

/// SERVTD_EXT_STRUCT fields in target TD’s TDCS
pub const TDCS_FIELD_SERVTD_INIT_SERVTD_INFO_HASH: u64 = 0x191000030000020E;
pub const TDCS_FIELD_SERVTD_INIT_ATTR: u64 = 0x191000030000020D;
Expand Down Expand Up @@ -75,10 +80,25 @@ pub struct TeeModel {
reservtd: [u8; 8],
}

pub fn read_servtd_ext(
binding_handle: u64,
target_td_uuid: &[u64],
) -> Result<ServtdExt, MigrationResult> {
/// Try to read ServtdExt from the target TD's TDCS.
/// Returns `None` if the target TD does not support SERVTD_EXT
/// (i.e., TDCS.ATTRIBUTES.SERVTDEXT bit 17 is zero).
pub fn read_servtd_ext(binding_handle: u64, target_td_uuid: &[u64]) -> Option<ServtdExt> {
// Check TDCS.ATTRIBUTES bit 17 (SERVTDEXT) to determine support.
let attributes = tdcall_servtd_rd(binding_handle, TDCS_FIELD_ATTRIBUTES, target_td_uuid)
.map_err(|e| {
log::error!("Failed to read TDCS.ATTRIBUTES: {e:?}\n");
e
})
.ok()?;
if (attributes.content & ATTRIBUTES_SERVTDEXT_BIT) == 0 {
log::info!(
"Target TD does not support SERVTD_EXT (ATTRIBUTES=0x{:x}).\n",
attributes.content
);
return None;
}

let read_field =
|field_base: u64, elem_size: usize, buf: &mut [u8]| -> Result<(), MigrationResult> {
for (idx, chunk) in buf.chunks_mut(elem_size).enumerate() {
Expand All @@ -99,19 +119,24 @@ pub fn read_servtd_ext(
let mut cur_servtd_info_hash = [0u8; 48];
let mut cur_servtd_attr = [0u8; 8];

read_field(
if read_field(
TDCS_FIELD_SERVTD_INIT_SERVTD_INFO_HASH,
8,
&mut init_servtd_info_hash,
)?;
read_field(TDCS_FIELD_SERVTD_INIT_ATTR, 8, &mut init_attr)?;
read_field(TDCS_FIELD_INIT_CPUSVN, 8, &mut init_cpusvn)?;
read_field(TDCS_FIELD_INIT_TEE_TCB_SVN, 8, &mut init_tee_tcb_svn)?;
read_field(TDCS_FIELD_INIT_TEE_MODEL, 4, &mut init_tee_model)?;
read_field(TDCS_FIELD_SERVTD_INFO_HASH, 8, &mut cur_servtd_info_hash)?;
read_field(TDCS_FIELD_SERVTD_ATTR, 8, &mut cur_servtd_attr)?;

Ok(ServtdExt {
)
.is_err()
|| read_field(TDCS_FIELD_SERVTD_INIT_ATTR, 8, &mut init_attr).is_err()
|| read_field(TDCS_FIELD_INIT_CPUSVN, 8, &mut init_cpusvn).is_err()
|| read_field(TDCS_FIELD_INIT_TEE_TCB_SVN, 8, &mut init_tee_tcb_svn).is_err()
|| read_field(TDCS_FIELD_INIT_TEE_MODEL, 4, &mut init_tee_model).is_err()
|| read_field(TDCS_FIELD_SERVTD_INFO_HASH, 8, &mut cur_servtd_info_hash).is_err()
|| read_field(TDCS_FIELD_SERVTD_ATTR, 8, &mut cur_servtd_attr).is_err()
{
log::error!("Failed to read SERVTD_EXT fields.\n");
return None;
}

Some(ServtdExt {
init_servtd_info_hash,
init_attr,
init_cpusvn,
Expand All @@ -124,12 +149,20 @@ pub fn read_servtd_ext(
})
}

pub fn write_approved_servtd_ext_hash(servtd_ext_hash: &[u8]) -> Result<(), MigrationResult> {
if servtd_ext_hash.len() != SHA384_DIGEST_SIZE {
/// Write the approved SERVTD_EXT hash to the target TD's TDCS.
/// If `servtd_ext_hash` is `None`, this is a no-op (target TD does not support SERVTD_EXT).
pub fn write_approved_servtd_ext_hash(
servtd_ext_hash: Option<&[u8]>,
) -> Result<(), MigrationResult> {
let hash = match servtd_ext_hash {
Some(h) => h,
None => return Ok(()),
};
if hash.len() != SHA384_DIGEST_SIZE {
return Err(MigrationResult::InvalidParameter);
}

for (idx, chunk) in servtd_ext_hash.chunks_exact(size_of::<u64>()).enumerate() {
for (idx, chunk) in hash.chunks_exact(size_of::<u64>()).enumerate() {
let elem = u64::from_le_bytes(chunk.try_into().unwrap());
tdcall_vm_write(
TDCS_FIELD_SERVTD_ACCEPT_SERVTD_EXT_HASH + idx as u64,
Expand All @@ -141,6 +174,7 @@ pub fn write_approved_servtd_ext_hash(servtd_ext_hash: &[u8]) -> Result<(), Migr
Ok(())
}

#[cfg(test)]
mod test {
use super::ServtdExt;

Expand Down
Loading
Loading