Verifying a Trusted Execution Domain
Every TED runs inside an Intel TDX trust domain. The platform publishes enough evidence for an independent party to check, without trusting Betteredge, that a given domain booted the expected firmware in the expected configuration on genuine, up-to-date Intel hardware.
This page describes how to reproduce that check offline. It needs no TDX hardware and no TPM - an ordinary Linux workstation is enough.
What the evidence proves
| Evidence | Establishes |
|---|---|
| TDX quote | Signed by Intel-rooted keys: the platform is genuine TDX, its TCB level, and the values of MRTD and RTMR0–3 at quote time |
| System firmware (OVMF) | The exact firmware image the domain boots from; MRTD and the CFV digest are recomputed from it |
| CC event log (CCEL) | The individual boot measurements whose replay produces RTMR0–2; secure boot state and key databases |
What it does not prove: anything about the domain after the measured boot completed, the behaviour of workloads running inside it, or the physical security of the site.
Collecting the evidence
System firmware - TED > Trust > System Firmware > OVMF firmware.
Quote - either download the quote attached to a key request under System Boot > Download quote, or generate a fresh one inside the domain. A self-generated quote carries a self-chosen nonce, which rules out replay:
head -c 64 /dev/urandom > nonce.bin
mkdir /sys/kernel/config/tsm/report/r0
dd if=nonce.bin of=/sys/kernel/config/tsm/report/r0/inblob bs=64
cat /sys/kernel/config/tsm/report/r0/outblob > quote.dat
rmdir /sys/kernel/config/tsm/report/r0
xxd -p -c 64 nonce.bin # keep for the reportData check- Event log - copied out of the domain:
cat /sys/firmware/acpi/tables/data/CCEL > ccel.binIf a downloaded quote arrives base64-encoded, decode it before use:
base64 -d quote.b64 > quote.datTools
We provide the td-verify CLI to automate these checks. The TED could also be independently verified with the following tools.
| Purpose | Tool | Install |
|---|---|---|
| Quote signature, PCK chain, QE identity, TCB | go-tdx-guest check | go install github.com/google/go-tdx-guest/tools/check@latest |
| Same as above, reference implementation | Intel DCAP libsgx-dcap-quote-verify + QuoteVerificationSample | Intel apt repository, C++ build |
| MRTD / RTMR precomputation | virtee/tdx-measure (Rust), cmc/mrtool (Go) | cargo install --git … / go install …/tools/mrtool@latest |
| In-guest inspection: Event log replay, MRTD, CFV, secure boot | tdeventlog (Canonical tdx-tools-guest), cc-api/cc-measure | distro package / source setupenv.sh |
Verification cli
Every check below is performed by td-verify, a single static binary built from go-trust. The tool is MIT-licensed library and the platform itself uses for attestation verification:
go install gitlab.com/real-cis/cc/go-trust/cmd/td-verify@latestOnly the quote is required - it is the hardware-signed root that everything else is compared against:
td-verify --quote quote.datEach further artefact enables further checks. Anything not supplied is reported as skipped, never as passed:
| Flag | Adds |
|---|---|
--firmware OVMF.fd | MRTD recomputation, CFV digest, secure boot variable digests |
--ccel ccel.bin | RTMR0–2 replay, secure boot state, CFV cross-check |
--nonce <hex> | reportData equals a caller-chosen challenge |
Other flags: --qemu-compat (default true, see below), --skip-signature for a fully offline run, --verbose to list every replayed event.
A full run:
td-verify --quote quote.dat \
--firmware OVMF.fd \
--ccel ccel.bin \
--nonce "$(xxd -p -c 64 nonce.bin)"1. Verify the quote
The first check validates the ECDSA signature, the PCK certificate chain up to the Intel SGX Root CA, certificate revocation, the QE identity, and the platform TCB level, using collateral fetched from Intel's provisioning service.
Until this passes, nothing else in the quote means anything - MRTD and the RTMRs are only trustworthy because Intel's key attests to them.
Independent cross-check with a third-party implementation:
go install github.com/google/go-tdx-guest/tools/check@latest
check -in quote.dat -inform bin -get_collateral -check_crlIntel's DCAP libsgx-dcap-quote-verify with the bundled QuoteVerificationSample is the reference implementation
2. Compare firmware measurements with the quote
MRTD is the build-time measurement the TDX module computes over the pages the VMM adds before the domain starts. For TDVF those pages are named in the firmware image's own metadata table, so MRTD can be recomputed offline from the image alone.
The Configuration Firmware Volume (CFV) holds the secure boot variables. It is page-added but not extended into MRTD; TDVF hashes it during boot and extends RTMR0 instead. Betteredge uses MRTD and the CFV digest as the seed for key derivation, which is why both appear on the Trust tab.
With --firmware, td-verify recomputes MRTD, the CFV digest and the PK, KEK, db and dbx measurements, then compares them against the quote and the event log.
QEMU 8.x and --qemu-compat
QEMU changed how TDVF sections are processed for the initial page add, which changes the resulting MRTD for an unchanged firmware image (qemu-tdx#1, td-shim#683). The current platform runs QEMU 8.x, so --qemu-compat defaults to true and no flag is needed. Hosts on QEMU 10.x or later need --qemu-compat=false; a MRTD mismatch says which value to try.
WARNING
Third-party MRTD tools (virtee/tdx-measure, cmc/mrtool, dstack-mr, td-shim-tee-info-hash) implement the post-8.x behaviour only and will report a different MRTD for the same image on the current fleet. A mismatch from one of those tools indicates the QEMU version difference, not a tampered firmware image. They remain a valid cross-check for RTMR values, and for MRTD once hosts are on QEMU 10.x.
3. Check secure boot state
Secure boot state lives in the event log, not the quote. The relevant record is an EV_EFI_VARIABLE_DRIVER_CONFIG event in RTMR0 carrying the SecureBoot variable, which must hold 0x01.
Secure boot being enabled is necessary but not sufficient: PK, KEK, db and dbx decide which images were allowed to load. With both --firmware and --ccel, td-verify also checks that the variables measured at boot are the ones baked into the image, which ties the key databases back to a downloadable artefact rather than to digests displayed in a dashboard. Those digests must still match a known-good set, and dbx must be current.
4. Replay the event log against the RTMRs
Each measurement extends a register with RTMR[i] = SHA384(RTMR[i] || digest), starting from zero. Replaying the log in order must reproduce the register values inside the signed quote. The CC event log records the measurement register index rather than a PCR number:
| CC MR index | TDX register | Contents |
|---|---|---|
| 0 | MRTD | firmware code (build time, not in the log) |
| 1 | RTMR0 | TD HOB, CFV, secure boot variables, VMM configuration |
| 2 | RTMR1 | kernel / initrd / boot parameters |
| 3 | RTMR2 | OS-level components |
A match binds the whole log to hardware-signed values: the log cannot be forged without breaking SHA-384, so every individual event in it becomes evidence. --verbose lists each replayed event, which localises a mismatch to a specific boot component.
RTMR3 is reserved for runtime extensions and is not covered by the boot log. A non-zero RTMR3 is legitimate; it must be checked against the signed quote directly, never against the log.
In-guest alternatives for inspecting the log interactively: tdeventlog from Canonical's tdx/guest-tools, or cc-api/cc-measure (tdx_eventlogs, tdx_rtmr, tdx_verify_rtmr).
5. Remaining checks in the quote body
Enforced automatically:
TD_ATTRIBUTES.DEBUGclear - a debuggable TD offers no confidentiality.MRSIGNERSEAMall zero - the TDX module is Intel-signed.MRCONFIGID,MROWNER,MROWNERCONFIG- expected to be zero unless the platform sets them deliberately.
reportData and freshness
reportData is 64 bytes chosen by whoever requested the quote.
For a quote generated inside the domain, it is exactly the nonce written to inblob, and --nonce checks it. This is the only way to prove a quote describes the domain now.
For a quote downloaded from the dashboard, reportData binds a random challenge issued by the key derivation service during the key request. That challenge proves freshness to the service at the time of the request, but it is not predictable from outside, so there is no reference value to compare against - the check is skipped. A downloaded quote is evidence about a past boot, dated by its journal entry; generating a fresh quote is what establishes the current state.
Worth reviewing by hand
TEE_TCB_SVNandMRSEAM- the TDX module version. These are checked against Intel's TCB info in step 1, which fails outright on a TCB level Intel no longer considers current, so the values are printed for the record rather than for a manual decision.XFAM- the extended features the domain may use.
Interpreting failures
| Symptom | Likely cause |
|---|---|
| Quote signature or TCB check fails | Signature, certificate chain or TCB rejected - not a genuine or not an up-to-date platform |
| MRTD differs | Different firmware image, or the wrong --qemu-compat setting for the host |
| MRTD differs only under a third-party tool | QEMU 8.x page-add behaviour; see --qemu-compat above |
| RTMR0 differs, CFV digest matches | Different VMM configuration, boot variables or TD HOB |
| RTMR1 or RTMR2 differ | Different kernel, initrd or command line |
| CFV digest differs | Different secure boot variable set baked into the firmware |
| Secure boot variables differ from the image | The domain booted with key databases other than the published ones |
| Event log parses but replay fails | Log truncated or tampered; the LogAreaMinimumLength field of /sys/firmware/acpi/tables/CCEL gives its recorded length |
References
- Intel TDX Module Base Architecture Specification - MRTD and RTMR semantics
- Intel TDX Virtual Firmware Design Guide - TDVF metadata, BFV/CFV, measurement flow
- UEFI Specification - CC event log format and register mapping
- TCG PC Client Platform Firmware Profile - event types and structures