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
63 changes: 63 additions & 0 deletions src/migtd/src/spdm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,36 @@ pub fn spdm_verify_quote(#[allow(unused_variables)] quote: &[u8]) -> SpdmResult<
})
}

/// Tear down the SPDM session identified by `session_id` (if it still
/// exists) and return `status` unchanged.
///
/// Use this on every security-relevant error path that runs after key
/// exchange. Without teardown the keyed session lingers in the
/// `SpdmContext`:
/// * On the responder, `rsp_handle_message` only breaks out of its dispatch
/// loop when the session disappears or the handler returns a
/// `StatusCode::VDM(_)` / `SPDM_STATUS_INVALID_STATE_LOCAL` error. Any
/// other `CORE(_)` status (e.g. `INVALID_MSG_FIELD`, `INVALID_MSG_SIZE`,
/// `BUFFER_FULL`, `CRYPTO_ERROR`) is silently swallowed and the loop
/// keeps polling on the *still-keyed* session.
/// * On the requester, the caller typically drops the context on `Err`, but
/// tearing the session down explicitly zeroes the established secrets
/// immediately and matches the responder convention.
///
/// The function is a no-op if `session_id` no longer maps to a live
/// session (idempotent), so it is safe to call after another teardown or
/// on pre-keying error paths.
fn fail_with_teardown(
ctx: &mut spdmlib::common::SpdmContext,
session_id: u32,
status: SpdmStatus,
) -> SpdmStatus {
if let Some(s) = ctx.get_session_via_id(session_id) {
s.teardown();
}
status
}

/// Verify that the peer's REPORTDATA is bound to the expected prefix and TH1.
pub fn verify_report_data_binding(
supplemental_data: &[u8],
Expand All @@ -225,6 +255,39 @@ pub fn verify_report_data_binding(
verify_peer_report_data(supplemental_data, &report_data)
}

/// Verify that a TDREPORT's REPORTDATA is bound to the expected prefix and TH1.
///
/// This is the rebind-path counterpart of [`verify_report_data_binding`], which
/// operates on quote supplemental data. In the rebind flow the peer provides a
/// raw TDREPORT instead of a quote; the REPORTDATA field is accessed via the
/// parsed `TdxReport.report_mac.report_data` struct field.
pub fn verify_tdreport_data_binding(
tdreport_bytes: &[u8],
peer_prefix: &[u8],
th1: &SpdmDigestStruct,
) -> Result<(), MigrationResult> {
use scroll::Pread;
use tdx_tdcall::tdreport::TdxReport;

const REPORT_DATA_HASH_SIZE: usize = 48;

let tdreport: TdxReport = tdreport_bytes.pread(0).map_err(|_| {
error!("Failed to parse TDREPORT\n");
MigrationResult::InvalidParameter
})?;

let expected_report_data =
build_report_data(peer_prefix, th1).map_err(|_| MigrationResult::InvalidParameter)?;
let expected_hash = digest_sha384(&expected_report_data)?;
let actual = &tdreport.report_mac.report_data[..REPORT_DATA_HASH_SIZE];

if actual != expected_hash.as_slice() {
return Err(MigrationResult::InvalidParameter);
}

Ok(())
}

const ECDSA_P384_SHA384_PRIVATE_KEY_LENGTH: usize = 0xb9;

#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop, Eq, PartialEq)]
Expand Down
Loading
Loading