From e98713ed143be09ba8dd15ac9a30e03c3da2f713 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Mon, 24 Aug 2026 18:39:17 -0700 Subject: [PATCH] fix(quote): align TD attributes with TDX 1.5 ABI --- cli/src/bin/generate_all_samples.rs | 4 +- dcap-qvl-js/src/verify.js | 50 ++++++----- src/quote.rs | 135 +++++++++++++++++++++------- src/verify.rs | 14 +-- 4 files changed, 141 insertions(+), 62 deletions(-) diff --git a/cli/src/bin/generate_all_samples.rs b/cli/src/bin/generate_all_samples.rs index 6d4d95e..97fc383 100644 --- a/cli/src/bin/generate_all_samples.rs +++ b/cli/src/bin/generate_all_samples.rs @@ -1061,8 +1061,8 @@ fn main() -> Result<()> { quote_generator: Box::new(|| { let header = create_sgx_header(4, 2, 0x00000081); let mut report = create_tdx_report(); - // Set reserved bit 29 (byte 3, bit 5) - report.td_attributes[3] |= 0x20; // Reserved bit 29 + // Set RESERVED_N bit 23 (byte 2, bit 7). + report.td_attributes[2] |= 0x80; let pck_cert = fs::read_to_string(format!("{}/pck.pem", CERT_DIR))?; let root_cert = fs::read_to_string(format!("{}/root_ca.pem", CERT_DIR))?; diff --git a/dcap-qvl-js/src/verify.js b/dcap-qvl-js/src/verify.js index 4686c2a..c8e4dbb 100644 --- a/dcap-qvl-js/src/verify.js +++ b/dcap-qvl-js/src/verify.js @@ -503,17 +503,19 @@ function validateSgx(report, allowDebug = false) { function validateTd10(report, allowDebug = false) { const tdAttrs = parseTdAttributes(report.tdAttributes); - // TUD bit 0 is DEBUG; bits 7:1 are reserved and must always be zero. - if ((tdAttrs.tud & ~0x01) !== 0) { - throw new Error('Reserved bits in TD attributes are set'); - } if ((tdAttrs.tud & 0x01) !== 0 && !allowDebug) { throw new Error('Debug mode is enabled'); } + if ((tdAttrs.tud & 0x70) !== 0) { + throw new Error('TD profiling is enabled'); + } - if (tdAttrs.sec.reservedLower !== 0 || tdAttrs.sec.reservedBit29 || tdAttrs.other.reserved !== 0) { + if (tdAttrs.reserved !== 0n) { throw new Error('Reserved bits in TD attributes are set'); } + if (tdAttrs.sec.migratable) { + throw new Error('TD migration is enabled'); + } if (!tdAttrs.sec.septVeDisable) { throw new Error('SEPT_VE_DISABLE is not enabled'); @@ -531,33 +533,35 @@ function validateTd15(report, allowDebug = false, allowServiceTd = false) { validateTd10(report.base, allowDebug); } -function parseTdAttributes(input) { - const tud = input[0]; +function ones(start, end) { + const first = BigInt(start); + const last = BigInt(end); + return ((1n << (last - first + 1n)) - 1n) << first; +} - // Extract SEC flags - const reservedLower = ((input[3] & 0x0f) << 16) | (input[2] << 8) | input[1]; - const septVeDisable = (input[3] & 0x10) !== 0; - const reservedBit29 = (input[3] & 0x20) !== 0; - const pks = (input[3] & 0x40) !== 0; - const kl = (input[3] & 0x80) !== 0; +// Intel TDX Module ABI Specification 348551-008US, Table 3.23: +// https://www.intel.com/content/www/us/en/content-details/865802/intel-tdx-module-abi-specification.html +const TD_ATTRIBUTES_RESERVED_MBZ_MASK = + ones(1, 3) | ones(7, 15) | ones(23, 26) | ones(32, 61); - // Extract OTHER flags - const reservedOther = ((input[7] & 0x7f) << 24) | (input[6] << 16) | (input[5] << 8) | input[4]; - const perfmon = (input[7] & 0x80) !== 0; +function parseTdAttributes(input) { + const bytes = Uint8Array.from(input); + const attributes = new DataView(bytes.buffer).getBigUint64(0, true); + const isSet = bit => (attributes & ones(bit, bit)) !== 0n; + const tud = input[0]; return { tud, sec: { - reservedLower, - septVeDisable, - reservedBit29, - pks, - kl, + septVeDisable: isSet(28), + migratable: isSet(29), + pks: isSet(30), + kl: isSet(31), }, other: { - reserved: reservedOther, - perfmon, + perfmon: isSet(63), }, + reserved: attributes & TD_ATTRIBUTES_RESERVED_MBZ_MASK, }; } diff --git a/src/quote.rs b/src/quote.rs index d86c153..470078d 100644 --- a/src/quote.rs +++ b/src/quote.rs @@ -150,8 +150,26 @@ pub struct TDAttributes { /// OTHER attributes that do not impact the security of the TD (bits 63:32) pub other: OTHERFlags, + + /// Bits that the TDX 1.5 ABI requires attestation verifiers to reject. + pub reserved: u64, +} + +const fn ones(range: core::ops::RangeInclusive) -> u64 { + let start = *range.start(); + let end = *range.end(); + let right_shift = match 63_u32.checked_sub(end) { + Some(shift) => shift, + None => panic!("bit range exceeds u64"), + }; + (u64::MAX << start) & (u64::MAX >> right_shift) } +// Intel TDX Module ABI Specification 348551-008US, Table 3.23: +// https://www.intel.com/content/www/us/en/content-details/865802/intel-tdx-module-abi-specification.html +const TD_ATTRIBUTES_RESERVED_MBZ_MASK: u64 = + ones(1..=3) | ones(7..=15) | ones(23..=26) | ones(32..=61); + /// TUD (TD Under Debug) flags (bits 7:0) #[derive(Debug, Clone)] pub struct TUDFlags { @@ -166,27 +184,36 @@ pub struct TUDFlags { /// SEC attributes that may impact the security of the TD (bits 31:8) #[derive(Debug, Clone)] pub struct SECFlags { - /// Reserved for future SEC flags - must be 0 (bits 27:8) - pub reserved_lower: u32, + /// ICSSD: Enable instruction-count based single-step defense + pub icssd: bool, + + /// SERVTD_EXT: Include a hash of SERVTD_EXT_STRUCT in TDREPORT_STRUCT + pub servtd_ext: bool, + + /// Positive reserved flags that attestation verifiers may accept (bits 22:18) + pub reserved_positive: u64, + + /// LASS: TD is allowed to use Linear Address Space Separation + pub lass: bool, /// SEPT_VE_DISABLE: Disable EPT violation conversion to #VE on TD access of PENDING pages pub sept_ve_disable: bool, - /// Reserved for future SEC flags - must be 0 (bit 29) - pub reserved_bit29: bool, + /// MIGRATABLE: TD is migratable using a Migration TD + pub migratable: bool, /// PKS: TD is allowed to use Supervisor Protection Keys pub pks: bool, - /// KL: TD is allowed to use Key Locker - pub kl: bool, + /// Positive reserved bit (formerly KL) + pub reserved_positive_bit31: bool, } /// OTHER attributes that do not impact the security of the TD (bits 63:32) #[derive(Debug, Clone)] pub struct OTHERFlags { - /// Reserved for future OTHER flags - must be 0 (bits 62:32) - pub reserved: u32, + /// TPA: TD is a TDX Connect Provisioning Agent + pub tpa: bool, /// PERFMON: TD is allowed to use Perfmon and PERF_METRICS capabilities pub perfmon: bool, @@ -194,40 +221,88 @@ pub struct OTHERFlags { impl TDAttributes { pub fn parse(input: [u8; 8]) -> Result { + let attributes = u64::from_le_bytes(input); + let is_set = |bit: u32| attributes & ones(bit..=bit) != 0; let tud = input[0]; - // Extract SEC flags (27:8 bits, bytes 1-3 and part of byte 4) - let reserved_lower = - (((input[3] & 0x0f) as u32) << 16) | ((input[2] as u32) << 8) | (input[1] as u32); - let sept_ve_disable = (input[3] & 0x10) != 0; // Bit 28 - let reserved_bit29 = (input[3] & 0x20) != 0; // Bit 29 - let pks = (input[3] & 0x40) != 0; // Bit 30 - let kl = (input[3] & 0x80) != 0; // Bit 31 - - // Extract OTHER flags (bytes 4-7) - // Mask bit 7 of input[7] (= PERFMON, bit 63) out of reserved_other. - let reserved_other = (((input[7] as u32) & 0x7F) << 24) - | ((input[6] as u32) << 16) - | ((input[5] as u32) << 8) - | (input[4] as u32); - let perfmon = (input[7] & 0x80) != 0; // Bit 63 + let icssd = is_set(16); + let servtd_ext = is_set(17); + let reserved_positive = attributes & ones(18..=22); + let lass = is_set(27); + let sept_ve_disable = is_set(28); + let migratable = is_set(29); + let pks = is_set(30); + let reserved_positive_bit31 = is_set(31); + + let tpa = is_set(62); + let perfmon = is_set(63); Ok(TDAttributes { tud, sec: SECFlags { - reserved_lower, + icssd, + servtd_ext, + reserved_positive, + lass, sept_ve_disable, - reserved_bit29, + migratable, pks, - kl, - }, - other: OTHERFlags { - reserved: reserved_other, - perfmon, + reserved_positive_bit31, }, + other: OTHERFlags { tpa, perfmon }, + reserved: attributes & TD_ATTRIBUTES_RESERVED_MBZ_MASK, }) } } +#[cfg(test)] +mod td_attributes_tests { + use super::{ones, TDAttributes}; + + #[test] + fn accepts_all_non_mbz_bits_from_tdx_1_5() { + let allowed = [ + 0_u32, 4, 5, 6, 16, 17, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 62, 63, + ]; + + for bit in allowed { + let attributes = TDAttributes::parse(ones(bit..=bit).to_le_bytes()).unwrap(); + assert_eq!(attributes.reserved, 0, "bit {bit} must be accepted"); + } + } + + #[test] + fn rejects_all_mbz_bits_from_tdx_1_5() { + let allowed_mask = [ + 0_u32, 4, 5, 6, 16, 17, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 62, 63, + ] + .into_iter() + .fold(0_u64, |mask, bit| mask | ones(bit..=bit)); + + for bit in 0..64 { + if allowed_mask & ones(bit..=bit) == 0 { + let attributes = TDAttributes::parse(ones(bit..=bit).to_le_bytes()).unwrap(); + assert_ne!(attributes.reserved, 0, "bit {bit} must be rejected"); + } + } + } + + #[test] + fn parses_new_tdx_1_5_flags() { + let value = ones(16..=22) | ones(27..=27) | ones(29..=29) | ones(31..=31) | ones(62..=63); + let attributes = TDAttributes::parse(value.to_le_bytes()).unwrap(); + + assert!(attributes.sec.icssd); + assert!(attributes.sec.servtd_ext); + assert_eq!(attributes.sec.reserved_positive, ones(18..=22)); + assert!(attributes.sec.lass); + assert!(attributes.sec.migratable); + assert!(attributes.sec.reserved_positive_bit31); + assert!(attributes.other.tpa); + assert!(attributes.other.perfmon); + assert_eq!(attributes.reserved, 0); + } +} + #[derive( Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize, )] diff --git a/src/verify.rs b/src/verify.rs index db4551b..b898e25 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1534,18 +1534,18 @@ fn validate_attrs(report: &Report, allow_service_td: bool, allow_debug: bool) -> fn validate_td10(report: &TDReport10, allow_debug: bool) -> Result<()> { let td_attrs = TDAttributes::parse(report.td_attributes).context("Failed to parse TD attributes")?; - if td_attrs.tud & !0x01 != 0 { - bail!("Reserved bits in TD attributes are set"); - } if td_attrs.tud & 0x01 != 0 && !allow_debug { bail!("Debug mode is enabled"); } - if td_attrs.sec.reserved_lower != 0 - || td_attrs.sec.reserved_bit29 - || td_attrs.other.reserved != 0 - { + if td_attrs.tud & 0x70 != 0 { + bail!("TD profiling is enabled"); + } + if td_attrs.reserved != 0 { bail!("Reserved bits in TD attributes are set"); } + if td_attrs.sec.migratable { + bail!("TD migration is enabled"); + } if !td_attrs.sec.sept_ve_disable { bail!("SEPT_VE_DISABLE is not enabled"); }