Skip to content

feat(os): expose TDX sealing key derivation to the guest - #1437

Draft
kvinwang wants to merge 1 commit into
nextfrom
feat/tdx-sealing-key
Draft

kvinwang wants to merge 1 commit into
nextfrom
feat/tdx-sealing-key

Conversation

@kvinwang

@kvinwang kvinwang commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

TDX module 1.5.34 and 2.0.18 add TDG.MR.KEY.GET (TDCALL leaf 29). It derives a key from a platform hardware secret and the TD measurements chosen by a key policy (MRTD, MROWNER, RTMR0-3, masked ATTRIBUTES/XFAM, salt). That is native TD sealing: the same measured TD on the same platform gets the same key, with no external key provider. Today the only local option is the local-sgx provider, which runs a Gramine enclave next to the VMM.

The dstack guest kernel (6.18.40) has no wrapper for this TDCALL and no way to reach it from user space. Upstream Linux has none either.

The TDX module enables the leaf for a TD only when one of these is set:

  • TD_PARAMS.CONFIG_FLAGS.SEALING, set by the VMM at TDH.MNG.INIT. That would need changes to QEMU and KVM on the host.
  • TDCS.TD_CTLS.ENABLE_HW_KEYS (bit 4). In the 1.5.34 and 2.0.18 metadata tables, TD_CTLS has prod_wr_mask = 0 and guest_wr_mask = 0x800000000000001F, so only the guest can write this bit.

So the feature can be enabled from the guest alone, with no host changes.

Fix

Add guest kernel patch 0011:

  • tdx_mcall_get_key() in arch/x86/coco/tdx/tdx.c wraps TDG.MR.KEY.GET. It first checks TDX_FEATURES0.HW_SEALING (bit 12) with TDG.SYS.RD. If the bit is clear it returns -EOPNOTSUPP, because the module answers TDX_OPERAND_INVALID to every request on a CPU without SEAMOPS(SEAMGETKEY). If the TDCALL returns TDX_MR_KEY_GET_NOT_SUPPORTED, it sets TD_CTLS.ENABLE_HW_KEYS with TDG.VM.WR and retries once. A VMM-provided CONFIG_FLAGS.SEALING therefore keeps working, and TD_CTLS is not touched when it is set.
  • A TDX_CMD_GET_KEY ioctl on /dev/tdx_guest (_IOWR('T', 0x10, struct tdx_key_req)) passes a raw 128-byte TDKEYREQUEST through and returns the key and its size in bits. The kernel does not interpret the request; the TDX module validates it. The key buffer is freed with kfree_sensitive().
  • Errors map as follows: invalid request → -EINVAL, requested SVN newer than the current TCB → -EPERM, busy → -EBUSY.

The ioctl number is 0x10 so it can't collide with Intel's downstream 'T' numbers (dstack's tdx-attest already uses 'T', 3 for the legacy RTMR extend).

Bit 12 of TDX_FEATURES0 is reserved in the 1.5.28 and 2.0.16 modules, so checking it is safe on older modules. On TDX 1.0, where TDG.SYS.RD is unavailable, the result is also -EOPNOTSUPP.

This PR only adds the guest OS capability. Nothing in dstack calls the ioctl yet, so key derivation and measurements of existing deployments do not change.

Verification

I built the mkosi kernel with patches 0001-0011 (checkpatch.pl: 0 errors, 0 warnings). I booted it as a throwaway TD with a static test /init: 1 vCPU, 1 GiB, launched by plain QEMU and not managed by dstack-vmm.

Host CPU / microcode TDX module TDX_FEATURES0.HW_SEALING TDX_CMD_GET_KEY
b200 Xeon 6767P (GNR) / 0x10003c2 2.0.18 (MRSEAM 2d2de102…3afa, TEE_TCB_SVN 0903) 0 -EOPNOTSUPP
tdx-office Xeon Gold 6530 (EMR) / 0x210002e0 1.5.28 (MRSEAM 346bc77a…31a1) 0 -EOPNOTSUPP

On both hosts TDX_CMD_GET_REPORT0 and the sysfs RTMR extend still work. Before I added the capability check, the b200 TD logged TDG.MR.KEY.GET → 0xC000010000000000 (TDX_OPERAND_INVALID), with TDX_FEATURES0 = 0x5ad9336f3f0fff, CONFIG_FLAGS = 0x5 (SEALING clear) and TD_CTLS = 0xf. That matches the module source: if (!global_data->sealing_supported) return TDX_OPERAND_INVALID;.

Not yet verified end to end. None of the machines available to me report HW_SEALING = 1, so the success path is untested on hardware: the ENABLE_HW_KEYS opt-in, key derivation, determinism, and policy/SVN behavior. b200's microcode is several revisions behind Intel's latest GNR release (0x1000434, microcode-20260811). Whether a newer microcode turns on SEAMGETKEY is unconfirmed. Upgrading it needs a host reboot, which I did not do on a production host. Keeping this as a draft until it runs on a platform that reports HW_SEALING = 1.

Follow-ups (separate PRs)

  • A tdx-attest binding plus a dstack-util probe.
  • A tdx-seal key provider as an alternative to local-sgx. This needs design decisions first:
    • Which SVN to derive for. Current CPUSVN/TEE_TCB_SVN break the disk key on every microcode or module update. Zero SVNs make the key derivable by older, possibly vulnerable TCBs.
    • The policy set. Mirroring local-sgx would be MRTD + RTMR0-3 at the same boot point.
    • A note that TDX module 2.0.18 accepts the MRCONFIGID policy bit but does not mix MRCONFIGID into the derivation.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant