Skip to content
Open
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
4 changes: 2 additions & 2 deletions cli/src/bin/generate_all_samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))?;
Expand Down
50 changes: 27 additions & 23 deletions dcap-qvl-js/src/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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,
};
}

Expand Down
135 changes: 105 additions & 30 deletions src/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>) -> 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 {
Expand All @@ -166,68 +184,125 @@ 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,
}

impl TDAttributes {
pub fn parse(input: [u8; 8]) -> Result<Self, scale::Error> {
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,
)]
Expand Down
14 changes: 7 additions & 7 deletions src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
Loading