Skip to content

Commit 8fafc3d

Browse files
committed
fix: reject debug TDs unconditionally, validate QE vendor ID, fix urlencode import
1 parent f304f46 commit 8fafc3d

5 files changed

Lines changed: 24 additions & 12 deletions

File tree

src/tinfoil/attestation/abi_tdx.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,12 @@ def _validate_header(header: TdxHeader) -> None:
688688
f"Invalid TEE type: 0x{header.tee_type:x}. Expected 0x{TEE_TDX:x} (TDX)."
689689
)
690690

691+
if header.qe_vendor_id != INTEL_QE_VENDOR_ID:
692+
raise TdxQuoteParseError(
693+
f"Unknown QE vendor ID: {header.qe_vendor_id.hex()}. "
694+
f"Expected Intel QE: {INTEL_QE_VENDOR_ID.hex()}"
695+
)
696+
691697

692698
def parse_quote(data: bytes) -> QuoteV4:
693699
"""

src/tinfoil/attestation/attestation_tdx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def verify_tdx_attestation_v2(attestation_doc: str) -> Verification:
278278
try:
279279
result = verify_tdx_attestation(attestation_doc, is_compressed=True)
280280
except TdxAttestationError as e:
281-
raise ValueError(f"TDX attestation verification failed: {e}")
281+
raise ValueError(f"TDX attestation verification failed: {e}") from e
282282

283283
measurement = Measurement(
284284
type=PredicateType.TDX_GUEST_V2,

src/tinfoil/attestation/validate_tdx.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,9 @@ def validate_td_attributes(td_attributes: bytes) -> None:
262262
"""
263263
Validate TD_ATTRIBUTES fixed bit constraints.
264264
265-
Note: DEBUG bit (bit 0) is enforced via exact byte matching of
266-
EXPECTED_TD_ATTRIBUTES, not via a separate check here.
265+
Per Intel DCAP spec Section 2.3.2: "Verify that all TD Under Debug
266+
flags (i.e., the TDATTRIBUTES.TUD field in the TD Quote Body) are
267+
set to zero."
267268
268269
Args:
269270
td_attributes: 8-byte TD_ATTRIBUTES from quote
@@ -278,7 +279,13 @@ def validate_td_attributes(td_attributes: bytes) -> None:
278279

279280
value = struct.unpack('<Q', td_attributes)[0]
280281

281-
# No FIXED1 bits to check for TD_ATTRIBUTES currently.
282+
# Mandatory: reject debug TDs unconditionally.
283+
# A debug TD has its memory visible to the host, defeating TDX
284+
# confidentiality guarantees.
285+
if value & TD_ATTRIBUTES_DEBUG_BIT:
286+
raise TdxValidationError(
287+
f"TD_ATTRIBUTES 0x{value:x}: DEBUG bit must not be set"
288+
)
282289

283290
# Check FIXED0 bits (only allowed bits may be set)
284291
if value & (~TD_ATTRIBUTES_FIXED0):

src/tinfoil/client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import random
88
from dataclasses import dataclass
99
from typing import Dict, Optional
10-
from urllib.parse import urlparse
10+
from urllib.parse import urlparse, urlencode
1111
import cryptography.x509
1212
from cryptography.hazmat.primitives.serialization import PublicFormat, Encoding
1313
import hashlib
1414

15-
from .attestation import fetch_attestation, PredicateType, TDX_TYPES
15+
from .attestation import fetch_attestation, TDX_TYPES
1616
from .attestation.attestation_tdx import verify_tdx_hardware
1717
from .github import fetch_latest_digest, fetch_attestation_bundle
1818
from .sigstore import verify_attestation, fetch_latest_hardware_measurements
@@ -248,7 +248,7 @@ def get_router_address(platform: Optional[str] = None) -> str:
248248
"""
249249
routers_url = "https://atc.tinfoil.sh/routers"
250250
if platform:
251-
routers_url += "?" + urllib.parse.urlencode({"platform": platform})
251+
routers_url += "?" + urlencode({"platform": platform})
252252

253253
try:
254254
with urllib.request.urlopen(routers_url, timeout=15) as response:

tests/test_validate_tdx.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,11 @@ def test_expected_td_attributes_value_passes(self):
126126
"""The expected TD_ATTRIBUTES value from Go passes validation."""
127127
validate_td_attributes(EXPECTED_TD_ATTRIBUTES) # Should not raise
128128

129-
def test_debug_bit_allowed_by_fixed0(self):
130-
"""DEBUG bit is allowed by FIXED0 (validation happens via exact match)."""
131-
# DEBUG bit is in FIXED0, so it passes fixed bit validation
132-
# The actual DEBUG rejection happens via exact byte matching
129+
def test_debug_bit_rejected_unconditionally(self):
130+
"""DEBUG bit is rejected unconditionally, even though it's in FIXED0."""
133131
td_attrs = struct.pack('<Q', TD_ATTRIBUTES_DEBUG_BIT)
134-
validate_td_attributes(td_attrs) # Should not raise (fixed bits allow it)
132+
with pytest.raises(TdxValidationError, match="DEBUG bit must not be set"):
133+
validate_td_attributes(td_attrs)
135134

136135
def test_unauthorized_bits_rejected(self):
137136
"""TD_ATTRIBUTES with bits outside FIXED0 mask is rejected."""

0 commit comments

Comments
 (0)