Skip to content

Commit e98713e

Browse files
committed
fix(quote): align TD attributes with TDX 1.5 ABI
1 parent a8cae62 commit e98713e

4 files changed

Lines changed: 141 additions & 62 deletions

File tree

cli/src/bin/generate_all_samples.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,8 +1061,8 @@ fn main() -> Result<()> {
10611061
quote_generator: Box::new(|| {
10621062
let header = create_sgx_header(4, 2, 0x00000081);
10631063
let mut report = create_tdx_report();
1064-
// Set reserved bit 29 (byte 3, bit 5)
1065-
report.td_attributes[3] |= 0x20; // Reserved bit 29
1064+
// Set RESERVED_N bit 23 (byte 2, bit 7).
1065+
report.td_attributes[2] |= 0x80;
10661066

10671067
let pck_cert = fs::read_to_string(format!("{}/pck.pem", CERT_DIR))?;
10681068
let root_cert = fs::read_to_string(format!("{}/root_ca.pem", CERT_DIR))?;

dcap-qvl-js/src/verify.js

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -503,17 +503,19 @@ function validateSgx(report, allowDebug = false) {
503503
function validateTd10(report, allowDebug = false) {
504504
const tdAttrs = parseTdAttributes(report.tdAttributes);
505505

506-
// TUD bit 0 is DEBUG; bits 7:1 are reserved and must always be zero.
507-
if ((tdAttrs.tud & ~0x01) !== 0) {
508-
throw new Error('Reserved bits in TD attributes are set');
509-
}
510506
if ((tdAttrs.tud & 0x01) !== 0 && !allowDebug) {
511507
throw new Error('Debug mode is enabled');
512508
}
509+
if ((tdAttrs.tud & 0x70) !== 0) {
510+
throw new Error('TD profiling is enabled');
511+
}
513512

514-
if (tdAttrs.sec.reservedLower !== 0 || tdAttrs.sec.reservedBit29 || tdAttrs.other.reserved !== 0) {
513+
if (tdAttrs.reserved !== 0n) {
515514
throw new Error('Reserved bits in TD attributes are set');
516515
}
516+
if (tdAttrs.sec.migratable) {
517+
throw new Error('TD migration is enabled');
518+
}
517519

518520
if (!tdAttrs.sec.septVeDisable) {
519521
throw new Error('SEPT_VE_DISABLE is not enabled');
@@ -531,33 +533,35 @@ function validateTd15(report, allowDebug = false, allowServiceTd = false) {
531533
validateTd10(report.base, allowDebug);
532534
}
533535

534-
function parseTdAttributes(input) {
535-
const tud = input[0];
536+
function ones(start, end) {
537+
const first = BigInt(start);
538+
const last = BigInt(end);
539+
return ((1n << (last - first + 1n)) - 1n) << first;
540+
}
536541

537-
// Extract SEC flags
538-
const reservedLower = ((input[3] & 0x0f) << 16) | (input[2] << 8) | input[1];
539-
const septVeDisable = (input[3] & 0x10) !== 0;
540-
const reservedBit29 = (input[3] & 0x20) !== 0;
541-
const pks = (input[3] & 0x40) !== 0;
542-
const kl = (input[3] & 0x80) !== 0;
542+
// Intel TDX Module ABI Specification 348551-008US, Table 3.23:
543+
// https://www.intel.com/content/www/us/en/content-details/865802/intel-tdx-module-abi-specification.html
544+
const TD_ATTRIBUTES_RESERVED_MBZ_MASK =
545+
ones(1, 3) | ones(7, 15) | ones(23, 26) | ones(32, 61);
543546

544-
// Extract OTHER flags
545-
const reservedOther = ((input[7] & 0x7f) << 24) | (input[6] << 16) | (input[5] << 8) | input[4];
546-
const perfmon = (input[7] & 0x80) !== 0;
547+
function parseTdAttributes(input) {
548+
const bytes = Uint8Array.from(input);
549+
const attributes = new DataView(bytes.buffer).getBigUint64(0, true);
550+
const isSet = bit => (attributes & ones(bit, bit)) !== 0n;
551+
const tud = input[0];
547552

548553
return {
549554
tud,
550555
sec: {
551-
reservedLower,
552-
septVeDisable,
553-
reservedBit29,
554-
pks,
555-
kl,
556+
septVeDisable: isSet(28),
557+
migratable: isSet(29),
558+
pks: isSet(30),
559+
kl: isSet(31),
556560
},
557561
other: {
558-
reserved: reservedOther,
559-
perfmon,
562+
perfmon: isSet(63),
560563
},
564+
reserved: attributes & TD_ATTRIBUTES_RESERVED_MBZ_MASK,
561565
};
562566
}
563567

src/quote.rs

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,26 @@ pub struct TDAttributes {
150150

151151
/// OTHER attributes that do not impact the security of the TD (bits 63:32)
152152
pub other: OTHERFlags,
153+
154+
/// Bits that the TDX 1.5 ABI requires attestation verifiers to reject.
155+
pub reserved: u64,
156+
}
157+
158+
const fn ones(range: core::ops::RangeInclusive<u32>) -> u64 {
159+
let start = *range.start();
160+
let end = *range.end();
161+
let right_shift = match 63_u32.checked_sub(end) {
162+
Some(shift) => shift,
163+
None => panic!("bit range exceeds u64"),
164+
};
165+
(u64::MAX << start) & (u64::MAX >> right_shift)
153166
}
154167

168+
// Intel TDX Module ABI Specification 348551-008US, Table 3.23:
169+
// https://www.intel.com/content/www/us/en/content-details/865802/intel-tdx-module-abi-specification.html
170+
const TD_ATTRIBUTES_RESERVED_MBZ_MASK: u64 =
171+
ones(1..=3) | ones(7..=15) | ones(23..=26) | ones(32..=61);
172+
155173
/// TUD (TD Under Debug) flags (bits 7:0)
156174
#[derive(Debug, Clone)]
157175
pub struct TUDFlags {
@@ -166,68 +184,125 @@ pub struct TUDFlags {
166184
/// SEC attributes that may impact the security of the TD (bits 31:8)
167185
#[derive(Debug, Clone)]
168186
pub struct SECFlags {
169-
/// Reserved for future SEC flags - must be 0 (bits 27:8)
170-
pub reserved_lower: u32,
187+
/// ICSSD: Enable instruction-count based single-step defense
188+
pub icssd: bool,
189+
190+
/// SERVTD_EXT: Include a hash of SERVTD_EXT_STRUCT in TDREPORT_STRUCT
191+
pub servtd_ext: bool,
192+
193+
/// Positive reserved flags that attestation verifiers may accept (bits 22:18)
194+
pub reserved_positive: u64,
195+
196+
/// LASS: TD is allowed to use Linear Address Space Separation
197+
pub lass: bool,
171198

172199
/// SEPT_VE_DISABLE: Disable EPT violation conversion to #VE on TD access of PENDING pages
173200
pub sept_ve_disable: bool,
174201

175-
/// Reserved for future SEC flags - must be 0 (bit 29)
176-
pub reserved_bit29: bool,
202+
/// MIGRATABLE: TD is migratable using a Migration TD
203+
pub migratable: bool,
177204

178205
/// PKS: TD is allowed to use Supervisor Protection Keys
179206
pub pks: bool,
180207

181-
/// KL: TD is allowed to use Key Locker
182-
pub kl: bool,
208+
/// Positive reserved bit (formerly KL)
209+
pub reserved_positive_bit31: bool,
183210
}
184211

185212
/// OTHER attributes that do not impact the security of the TD (bits 63:32)
186213
#[derive(Debug, Clone)]
187214
pub struct OTHERFlags {
188-
/// Reserved for future OTHER flags - must be 0 (bits 62:32)
189-
pub reserved: u32,
215+
/// TPA: TD is a TDX Connect Provisioning Agent
216+
pub tpa: bool,
190217

191218
/// PERFMON: TD is allowed to use Perfmon and PERF_METRICS capabilities
192219
pub perfmon: bool,
193220
}
194221

195222
impl TDAttributes {
196223
pub fn parse(input: [u8; 8]) -> Result<Self, scale::Error> {
224+
let attributes = u64::from_le_bytes(input);
225+
let is_set = |bit: u32| attributes & ones(bit..=bit) != 0;
197226
let tud = input[0];
198-
// Extract SEC flags (27:8 bits, bytes 1-3 and part of byte 4)
199-
let reserved_lower =
200-
(((input[3] & 0x0f) as u32) << 16) | ((input[2] as u32) << 8) | (input[1] as u32);
201-
let sept_ve_disable = (input[3] & 0x10) != 0; // Bit 28
202-
let reserved_bit29 = (input[3] & 0x20) != 0; // Bit 29
203-
let pks = (input[3] & 0x40) != 0; // Bit 30
204-
let kl = (input[3] & 0x80) != 0; // Bit 31
205-
206-
// Extract OTHER flags (bytes 4-7)
207-
// Mask bit 7 of input[7] (= PERFMON, bit 63) out of reserved_other.
208-
let reserved_other = (((input[7] as u32) & 0x7F) << 24)
209-
| ((input[6] as u32) << 16)
210-
| ((input[5] as u32) << 8)
211-
| (input[4] as u32);
212-
let perfmon = (input[7] & 0x80) != 0; // Bit 63
227+
let icssd = is_set(16);
228+
let servtd_ext = is_set(17);
229+
let reserved_positive = attributes & ones(18..=22);
230+
let lass = is_set(27);
231+
let sept_ve_disable = is_set(28);
232+
let migratable = is_set(29);
233+
let pks = is_set(30);
234+
let reserved_positive_bit31 = is_set(31);
235+
236+
let tpa = is_set(62);
237+
let perfmon = is_set(63);
213238

214239
Ok(TDAttributes {
215240
tud,
216241
sec: SECFlags {
217-
reserved_lower,
242+
icssd,
243+
servtd_ext,
244+
reserved_positive,
245+
lass,
218246
sept_ve_disable,
219-
reserved_bit29,
247+
migratable,
220248
pks,
221-
kl,
222-
},
223-
other: OTHERFlags {
224-
reserved: reserved_other,
225-
perfmon,
249+
reserved_positive_bit31,
226250
},
251+
other: OTHERFlags { tpa, perfmon },
252+
reserved: attributes & TD_ATTRIBUTES_RESERVED_MBZ_MASK,
227253
})
228254
}
229255
}
230256

257+
#[cfg(test)]
258+
mod td_attributes_tests {
259+
use super::{ones, TDAttributes};
260+
261+
#[test]
262+
fn accepts_all_non_mbz_bits_from_tdx_1_5() {
263+
let allowed = [
264+
0_u32, 4, 5, 6, 16, 17, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 62, 63,
265+
];
266+
267+
for bit in allowed {
268+
let attributes = TDAttributes::parse(ones(bit..=bit).to_le_bytes()).unwrap();
269+
assert_eq!(attributes.reserved, 0, "bit {bit} must be accepted");
270+
}
271+
}
272+
273+
#[test]
274+
fn rejects_all_mbz_bits_from_tdx_1_5() {
275+
let allowed_mask = [
276+
0_u32, 4, 5, 6, 16, 17, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 62, 63,
277+
]
278+
.into_iter()
279+
.fold(0_u64, |mask, bit| mask | ones(bit..=bit));
280+
281+
for bit in 0..64 {
282+
if allowed_mask & ones(bit..=bit) == 0 {
283+
let attributes = TDAttributes::parse(ones(bit..=bit).to_le_bytes()).unwrap();
284+
assert_ne!(attributes.reserved, 0, "bit {bit} must be rejected");
285+
}
286+
}
287+
}
288+
289+
#[test]
290+
fn parses_new_tdx_1_5_flags() {
291+
let value = ones(16..=22) | ones(27..=27) | ones(29..=29) | ones(31..=31) | ones(62..=63);
292+
let attributes = TDAttributes::parse(value.to_le_bytes()).unwrap();
293+
294+
assert!(attributes.sec.icssd);
295+
assert!(attributes.sec.servtd_ext);
296+
assert_eq!(attributes.sec.reserved_positive, ones(18..=22));
297+
assert!(attributes.sec.lass);
298+
assert!(attributes.sec.migratable);
299+
assert!(attributes.sec.reserved_positive_bit31);
300+
assert!(attributes.other.tpa);
301+
assert!(attributes.other.perfmon);
302+
assert_eq!(attributes.reserved, 0);
303+
}
304+
}
305+
231306
#[derive(
232307
Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
233308
)]

src/verify.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,18 +1534,18 @@ fn validate_attrs(report: &Report, allow_service_td: bool, allow_debug: bool) ->
15341534
fn validate_td10(report: &TDReport10, allow_debug: bool) -> Result<()> {
15351535
let td_attrs =
15361536
TDAttributes::parse(report.td_attributes).context("Failed to parse TD attributes")?;
1537-
if td_attrs.tud & !0x01 != 0 {
1538-
bail!("Reserved bits in TD attributes are set");
1539-
}
15401537
if td_attrs.tud & 0x01 != 0 && !allow_debug {
15411538
bail!("Debug mode is enabled");
15421539
}
1543-
if td_attrs.sec.reserved_lower != 0
1544-
|| td_attrs.sec.reserved_bit29
1545-
|| td_attrs.other.reserved != 0
1546-
{
1540+
if td_attrs.tud & 0x70 != 0 {
1541+
bail!("TD profiling is enabled");
1542+
}
1543+
if td_attrs.reserved != 0 {
15471544
bail!("Reserved bits in TD attributes are set");
15481545
}
1546+
if td_attrs.sec.migratable {
1547+
bail!("TD migration is enabled");
1548+
}
15491549
if !td_attrs.sec.sept_ve_disable {
15501550
bail!("SEPT_VE_DISABLE is not enabled");
15511551
}

0 commit comments

Comments
 (0)