Skip to content

Commit bb3ca0b

Browse files
refactor(attestation): verify policy against TDINFO_STRUCT
Adapt rebinding handshake to use init_tdinfo instead of init_policy: - rebinding.rs: rename params in pre_session_data_exchange functions - server_client.rs: rename init_td_report -> init_tdinfo in RATLS cert creation/verification, update pre_session_data parsing with init_tdinfo naming, compare mrowner at TDINFO offset 112..160 directly instead of digest_sha384(init_policy) - spdm_rsp.rs: rename pre_session_data parsing, compare mrowner directly Per GHCI 1.5, policy and SERVTD_EXT verification operates on TDINFO_STRUCT: - verify_servtd_hash(): accepts TDINFO bytes, returns TdInfo (not TdxReport), parses via MaybeUninit + copy_nonoverlapping - verify_init_tdreport() -> verify_init_tdinfo(): renamed, returns TdInfo - Add get_rtmrs_from_tdinfo() and setup_evaluation_data_with_tdinfo() - authenticate_rebinding_old(): 6 params instead of 7 (removed init_policy and init_td_report, replaced with init_tdinfo); calls verify_event_log() directly against RTMRs from init_tdinfo; uses local policy for TCB eval - Remove get_init_tcb_evaluation_info() and TD_INFO_OFFSET constant - Update call sites in server_client.rs and spdm_rsp.rs Co-authored-by: Grams, Stanislaw <stanislaw.grams@intel.com>
1 parent c35e102 commit bb3ca0b

4 files changed

Lines changed: 127 additions & 77 deletions

File tree

src/migtd/src/mig_policy.rs

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ mod v2 {
8787
const SERVTD_ATTR_IGNORE_RTMR3: u64 = 0x200_0000_0000;
8888

8989
const SERVTD_TYPE_MIGTD: u16 = 0;
90-
const TD_INFO_OFFSET: usize = 512;
9190

9291
lazy_static! {
9392
pub static ref LOCAL_TCB_INFO: Once<PolicyEvaluationInfo> = Once::new();
@@ -136,13 +135,6 @@ mod v2 {
136135
.ok_or(PolicyError::InvalidParameter)
137136
}
138137

139-
pub fn get_init_tcb_evaluation_info(
140-
init_report: &TdxReport,
141-
init_policy: &VerifiedPolicy,
142-
) -> Result<PolicyEvaluationInfo, PolicyError> {
143-
setup_evaluation_data_with_tdreport(init_report, init_policy)
144-
}
145-
146138
/// Get reference to the global verified policy
147139
/// Returns None if the policy hasn't been initialized yet
148140
pub fn get_verified_policy() -> Option<&'static VerifiedPolicy<'static>> {
@@ -273,13 +265,14 @@ mod v2 {
273265
}
274266

275267
// Authenticate the migtd-old from migtd-new side
268+
// Per GHCI 1.5: init_tdinfo is a TDINFO_STRUCT (not full TDREPORT),
269+
// and there is no separate init_policy JSON blob.
276270
pub fn authenticate_rebinding_old(
277271
tdreport_src: &[u8],
278272
event_log_src: &[u8],
279273
mig_policy_src: &[u8],
280-
init_policy: &[u8],
274+
init_tdinfo: &[u8],
281275
init_event_log: &[u8],
282-
init_td_report: &[u8],
283276
servtd_ext_src: &[u8],
284277
) -> Result<Vec<u8>, PolicyError> {
285278
let policy_issuer_chain = get_policy_issuer_chain().ok_or(PolicyError::InvalidParameter)?;
@@ -294,29 +287,30 @@ mod v2 {
294287
)?;
295288
let policy = get_verified_policy().ok_or(PolicyError::InvalidParameter)?;
296289

297-
// Verify the td report init / event log init / policy init
290+
// Verify the init tdinfo against servtd_ext hash
298291
let servtd_ext_src_obj =
299292
ServtdExt::read_from_bytes(servtd_ext_src).ok_or(PolicyError::InvalidParameter)?;
300-
let init_tdreport = verify_init_tdreport(init_td_report, &servtd_ext_src_obj)?;
293+
let init_td_info = verify_init_tdinfo(init_tdinfo, &servtd_ext_src_obj)?;
301294
let _engine_svn = policy
302295
.servtd_tcb_mapping
303296
.get_engine_svn_by_measurements(&Measurements::new_from_bytes(
304-
&init_tdreport.td_info.mrtd,
305-
&init_tdreport.td_info.rtmr0,
306-
&init_tdreport.td_info.rtmr1,
297+
&init_td_info.mrtd,
298+
&init_td_info.rtmr0,
299+
&init_td_info.rtmr1,
307300
None,
308301
None,
309302
))
310303
.ok_or(PolicyError::SvnMismatch)?;
311-
let verified_policy_init = verify_policy_and_event_log(
304+
305+
// Verify init event log integrity against RTMRs from init tdinfo
306+
verify_event_log(
312307
init_event_log,
313-
init_policy,
314-
policy_issuer_chain,
315-
&get_rtmrs_from_tdreport(&init_tdreport)?,
316-
)?;
308+
&get_rtmrs_from_tdinfo(&init_td_info)?,
309+
)
310+
.map_err(|_| PolicyError::InvalidEventLog)?;
317311

318-
let relative_reference =
319-
get_init_tcb_evaluation_info(&init_tdreport, &verified_policy_init)?;
312+
// Use local policy's tcb_mapping with init tdinfo measurements
313+
let relative_reference = setup_evaluation_data_with_tdinfo(&init_td_info, policy)?;
320314
policy.policy_data.evaluate_policy_common(
321315
&evaluation_data_src,
322316
&relative_reference,
@@ -466,51 +460,61 @@ mod v2 {
466460
Ok(tdx_report)
467461
}
468462

463+
/// Per GHCI 1.5: accepts TDINFO_STRUCT bytes directly (not full TDREPORT)
469464
fn verify_servtd_hash(
470-
servtd_report: &[u8],
465+
tdinfo_bytes: &[u8],
471466
servtd_attr: u64,
472467
init_servtd_hash: &[u8],
473-
) -> Result<TdxReport, PolicyError> {
474-
if servtd_report.len() < TD_INFO_OFFSET + size_of::<TdInfo>() {
468+
) -> Result<TdInfo, PolicyError> {
469+
if tdinfo_bytes.len() < size_of::<TdInfo>() {
475470
return Err(PolicyError::InvalidParameter);
476471
}
477472

478-
// Extract TdInfo from the report
479-
let mut td_report =
480-
TdxReport::read_from_bytes(servtd_report).ok_or(PolicyError::InvalidTdReport)?;
473+
// Parse TdInfo directly from bytes
474+
let mut td_info = {
475+
let mut uninit = core::mem::MaybeUninit::<TdInfo>::uninit();
476+
unsafe {
477+
core::ptr::copy_nonoverlapping(
478+
tdinfo_bytes.as_ptr(),
479+
uninit.as_mut_ptr() as *mut u8,
480+
size_of::<TdInfo>(),
481+
);
482+
uninit.assume_init()
483+
}
484+
};
481485

482486
if (servtd_attr & SERVTD_ATTR_IGNORE_ATTRIBUTES) != 0 {
483-
td_report.td_info.attributes.fill(0);
487+
td_info.attributes.fill(0);
484488
}
485489
if (servtd_attr & SERVTD_ATTR_IGNORE_XFAM) != 0 {
486-
td_report.td_info.xfam.fill(0);
490+
td_info.xfam.fill(0);
487491
}
488492
if (servtd_attr & SERVTD_ATTR_IGNORE_MRTD) != 0 {
489-
td_report.td_info.mrtd.fill(0);
493+
td_info.mrtd.fill(0);
490494
}
491495
if (servtd_attr & SERVTD_ATTR_IGNORE_MRCONFIGID) != 0 {
492-
td_report.td_info.mrconfig_id.fill(0);
496+
td_info.mrconfig_id.fill(0);
493497
}
494498
if (servtd_attr & SERVTD_ATTR_IGNORE_MROWNER) != 0 {
495-
td_report.td_info.mrowner.fill(0);
499+
td_info.mrowner.fill(0);
496500
}
497501
if (servtd_attr & SERVTD_ATTR_IGNORE_MROWNERCONFIG) != 0 {
498-
td_report.td_info.mrownerconfig.fill(0);
502+
td_info.mrownerconfig.fill(0);
499503
}
500504
if (servtd_attr & SERVTD_ATTR_IGNORE_RTMR0) != 0 {
501-
td_report.td_info.rtmr0.fill(0);
505+
td_info.rtmr0.fill(0);
502506
}
503507
if (servtd_attr & SERVTD_ATTR_IGNORE_RTMR1) != 0 {
504-
td_report.td_info.rtmr1.fill(0);
508+
td_info.rtmr1.fill(0);
505509
}
506510
if (servtd_attr & SERVTD_ATTR_IGNORE_RTMR2) != 0 {
507-
td_report.td_info.rtmr2.fill(0);
511+
td_info.rtmr2.fill(0);
508512
}
509513
if (servtd_attr & SERVTD_ATTR_IGNORE_RTMR3) != 0 {
510-
td_report.td_info.rtmr3.fill(0);
514+
td_info.rtmr3.fill(0);
511515
}
512516

513-
let info_hash = digest_sha384(td_report.td_info.as_bytes())
517+
let info_hash = digest_sha384(td_info.as_bytes())
514518
.map_err(|_| PolicyError::HashCalculation)?;
515519

516520
// Calculate ServTD hash: SHA384(info_hash || type || attr)
@@ -531,20 +535,62 @@ mod v2 {
531535
return Err(PolicyError::InvalidTdReport);
532536
}
533537

534-
Ok(td_report)
538+
Ok(td_info)
535539
}
536540

537-
fn verify_init_tdreport(
538-
init_report: &[u8],
541+
/// Per GHCI 1.5: verifies TDINFO_STRUCT against servtd_ext hash
542+
fn verify_init_tdinfo(
543+
init_tdinfo: &[u8],
539544
servtd_ext: &ServtdExt,
540-
) -> Result<TdxReport, PolicyError> {
545+
) -> Result<TdInfo, PolicyError> {
541546
verify_servtd_hash(
542-
init_report,
547+
init_tdinfo,
543548
u64::from_le_bytes(servtd_ext.init_attr),
544549
&servtd_ext.init_servtd_info_hash,
545550
)
546551
}
547552

553+
fn get_rtmrs_from_tdinfo(
554+
td_info: &TdInfo,
555+
) -> Result<[[u8; SHA384_DIGEST_SIZE]; 4], PolicyError> {
556+
let mut rtmrs = [[0u8; SHA384_DIGEST_SIZE]; 4];
557+
rtmrs[0].copy_from_slice(&td_info.rtmr0);
558+
rtmrs[1].copy_from_slice(&td_info.rtmr1);
559+
rtmrs[2].copy_from_slice(&td_info.rtmr2);
560+
rtmrs[3].copy_from_slice(&td_info.rtmr3);
561+
Ok(rtmrs)
562+
}
563+
564+
fn setup_evaluation_data_with_tdinfo(
565+
td_info: &TdInfo,
566+
policy: &VerifiedPolicy,
567+
) -> Result<PolicyEvaluationInfo, PolicyError> {
568+
let migtd_svn = policy.servtd_tcb_mapping.get_engine_svn_by_measurements(
569+
&Measurements::new_from_bytes(
570+
&td_info.mrtd,
571+
&td_info.rtmr0,
572+
&td_info.rtmr1,
573+
None,
574+
None,
575+
),
576+
);
577+
578+
let migtd_tcb = migtd_svn.and_then(|svn| policy.servtd_identity.get_tcb_level_by_svn(svn));
579+
580+
Ok(PolicyEvaluationInfo {
581+
tee_tcb_svn: None,
582+
tcb_date: None,
583+
tcb_status: None,
584+
tcb_evaluation_number: None,
585+
fmspc: None,
586+
migtd_isvsvn: migtd_svn,
587+
migtd_tcb_date: migtd_tcb.map(|tcb| tcb.tcb_date.clone()),
588+
migtd_tcb_status: migtd_tcb.map(|tcb| tcb.tcb_status.clone()),
589+
pck_crl_num: None,
590+
root_ca_crl_num: None,
591+
})
592+
}
593+
548594
fn setup_evaluation_data(
549595
fmspc: [u8; 6],
550596
suppl_data: &[u8],

src/migtd/src/migration/rebinding.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ impl<'a> MigtdDataEntry<'a> {
235235

236236
pub(super) async fn rebinding_old_pre_session_data_exchange(
237237
transport: &mut TransportType,
238-
init_policy: &[u8],
238+
init_tdinfo: &[u8],
239239
) -> Result<Vec<u8>, MigrationResult> {
240240
let version = exchange_hello_packet(transport).await.map_err(|e| {
241241
log::error!(
@@ -271,7 +271,7 @@ pub(super) async fn rebinding_old_pre_session_data_exchange(
271271
e
272272
})?;
273273

274-
send_pre_session_data_packet(init_policy, transport)
274+
send_pre_session_data_packet(init_tdinfo, transport)
275275
.await
276276
.map_err(|e| {
277277
log::error!(
@@ -336,11 +336,11 @@ pub(super) async fn rebinding_new_pre_session_data_exchange(
336336
e
337337
})?;
338338

339-
let init_policy = receive_pre_session_data_packet(transport)
339+
let init_tdinfo = receive_pre_session_data_packet(transport)
340340
.await
341341
.map_err(|e| {
342342
log::error!(
343-
"pre_session_data_exchange: send_pre_session_data_packet error: {:?}\n",
343+
"pre_session_data_exchange: receive init_tdinfo error: {:?}\n",
344344
e
345345
);
346346
e
@@ -365,8 +365,8 @@ pub(super) async fn rebinding_new_pre_session_data_exchange(
365365
let mut policy_buffer = Vec::new();
366366
policy_buffer.extend_from_slice(&(remote_policy.len() as u32).to_le_bytes());
367367
policy_buffer.extend_from_slice(&remote_policy);
368-
policy_buffer.extend_from_slice(&(init_policy.len() as u32).to_le_bytes());
369-
policy_buffer.extend_from_slice(&init_policy);
368+
policy_buffer.extend_from_slice(&(init_tdinfo.len() as u32).to_le_bytes());
369+
policy_buffer.extend_from_slice(&init_tdinfo);
370370

371371
Ok(policy_buffer)
372372
}

src/migtd/src/ratls/server_client.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ pub fn client_rebinding<T: AsyncRead + AsyncWrite + Unpin>(
184184
stream: T,
185185
remote_policy: Vec<u8>,
186186
init_policy_hash: &[u8],
187-
init_td_report: &[u8],
187+
init_tdinfo: &[u8],
188188
init_event_log: &[u8],
189189
servtd_ext: &ServtdExt,
190190
) -> Result<SecureChannel<T>> {
@@ -198,7 +198,7 @@ pub fn client_rebinding<T: AsyncRead + AsyncWrite + Unpin>(
198198
let certs = create_certificate_for_rebinding_old(
199199
&signing_key,
200200
init_policy_hash,
201-
init_td_report,
201+
init_tdinfo,
202202
init_event_log,
203203
servtd_ext,
204204
)
@@ -409,7 +409,7 @@ fn create_certificate_for_client(signing_key: &EcdsaPk) -> Result<(Vec<u8>, Vec<
409409
fn create_certificate_for_rebinding_old(
410410
signing_key: &EcdsaPk,
411411
init_policy_hash: &[u8],
412-
init_tdreport: &[u8],
412+
init_tdinfo: &[u8],
413413
init_event_log: &[u8],
414414
servtd_ext: &ServtdExt,
415415
) -> Result<Vec<u8>> {
@@ -508,7 +508,7 @@ fn create_certificate_for_rebinding_old(
508508
Extension::new(
509509
EXTNID_MIGTD_TDREPORT_INIT,
510510
Some(false),
511-
Some(&init_tdreport),
511+
Some(&init_tdinfo),
512512
)
513513
.map_err(|e| {
514514
log::error!(
@@ -992,16 +992,18 @@ mod verify {
992992
log::error!("Failed to find expected policy hash extension.\n");
993993
CryptoError::ParseCertificate
994994
})?;
995-
let init_td_report =
995+
// Per GHCI 1.5: init extension now carries TDINFO_STRUCT instead of full TDREPORT
996+
let init_tdinfo =
996997
find_extension(extensions, &EXTNID_MIGTD_TDREPORT_INIT).ok_or_else(|| {
997-
log::error!("Failed to find init tdreport extension.\n");
998+
log::error!("Failed to find init tdinfo extension.\n");
998999
CryptoError::ParseCertificate
9991000
})?;
10001001
let init_event_log =
10011002
find_extension(extensions, &EXTNID_MIGTD_EVENT_LOG_INIT).ok_or_else(|| {
10021003
log::error!("Failed to find init event log extension.\n");
10031004
CryptoError::ParseCertificate
10041005
})?;
1006+
// Per GHCI 1.5: init_policy_hash is now mrowner from the initial TDINFO_STRUCT
10051007
let init_policy_hash = find_extension(extensions, &EXTNID_MIGTD_INIT_POLICY_HASH)
10061008
.ok_or_else(|| {
10071009
log::error!("Failed to find init policy hash extension.\n");
@@ -1012,6 +1014,7 @@ mod verify {
10121014
CryptoError::ParseCertificate
10131015
})?;
10141016

1017+
// Parse pre_session_data: [remote_policy_size(4) | remote_policy | init_tdinfo_size(4) | init_tdinfo]
10151018
let remote_policy_size = u32::from_le_bytes(
10161019
pre_session_data
10171020
.get(..4)
@@ -1024,18 +1027,19 @@ mod verify {
10241027
let remote_policy = pre_session_data.get(4..4 + remote_policy_size).ok_or(
10251028
CryptoError::TlsVerifyPeerCert(INVALID_MIG_POLICY_ERROR.to_string()),
10261029
)?;
1027-
let init_policy_offset = 4 + remote_policy_size;
1028-
let init_policy_size = u32::from_le_bytes(
1030+
// Per GHCI 1.5: second item is init_tdinfo (was init_policy)
1031+
let init_tdinfo_offset = 4 + remote_policy_size;
1032+
let init_tdinfo_size = u32::from_le_bytes(
10291033
pre_session_data
1030-
.get(init_policy_offset..4 + init_policy_offset)
1034+
.get(init_tdinfo_offset..4 + init_tdinfo_offset)
10311035
.ok_or(CryptoError::TlsVerifyPeerCert(
10321036
INVALID_MIG_POLICY_ERROR.to_string(),
10331037
))?
10341038
.try_into()
10351039
.unwrap(),
10361040
) as usize;
1037-
let init_policy = pre_session_data
1038-
.get(init_policy_offset + 4..init_policy_offset + 4 + init_policy_size)
1041+
let _init_tdinfo_from_pre_session = pre_session_data
1042+
.get(init_tdinfo_offset + 4..init_tdinfo_offset + 4 + init_tdinfo_size)
10391043
.ok_or(CryptoError::TlsVerifyPeerCert(
10401044
INVALID_MIG_POLICY_ERROR.to_string(),
10411045
))?;
@@ -1046,9 +1050,10 @@ mod verify {
10461050
INVALID_MIG_POLICY_ERROR.to_string(),
10471051
));
10481052
}
1049-
let exact_init_policy_hash = digest_sha384(init_policy)?;
1050-
if init_policy_hash != exact_init_policy_hash.as_slice() {
1051-
log::error!("Invalid init rebinding policy.\n");
1053+
// Per GHCI 1.5: init_policy_hash is mrowner from TDINFO — compare directly
1054+
// (no longer a hash of a policy blob)
1055+
if init_policy_hash != init_tdinfo.get(112..160).unwrap_or(&[]) {
1056+
log::error!("Invalid init policy hash (mrowner mismatch).\n");
10521057
return Err(CryptoError::TlsVerifyPeerCert(
10531058
INVALID_MIG_POLICY_ERROR.to_string(),
10541059
));
@@ -1058,9 +1063,8 @@ mod verify {
10581063
td_report,
10591064
event_log,
10601065
remote_policy,
1061-
init_policy,
1066+
init_tdinfo,
10621067
init_event_log,
1063-
init_td_report,
10641068
servtd_ext,
10651069
);
10661070

0 commit comments

Comments
 (0)