feat(tdx): add TDX backend with configfs-tsm direct adapter#68
feat(tdx): add TDX backend with configfs-tsm direct adapter#680xHansLee wants to merge 2 commits into
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
d32137d to
1ab3d10
Compare
05004f6 to
12f1e6f
Compare
|
The PR is large and the input is truncated — High-confidence issue found: Review iteration 11 · Commit 89a3aa1 · 2026-06-22T02:28:08Z |
12f1e6f to
b504b4b
Compare
## Summary On-chain validator for TDX V4/V5 attestation quotes used by the DKG flow. Mirrors `SGXValidationHook` in structure (`Ownable2Step + Pausable`) with TDX-specific quote field offsets. ## Identity model (v3 — RTMR3-bound binary, RTMR2-included platform) Split along the operator / cloud boundary, where "operator-controlled" means *the running Go binary*: - **Binary identity** (operator-controlled): `keccak256(RTMR3)`, whitelisted via `EnclaveTypeData.codeCommitment` (same path as SGX `MRENCLAVE`). RTMR3 is self-extended by the kernel exactly once at startup with `SHA-384(/proc/self/exe)` (see [story-kernel #68](piplabs/story-kernel#68)), so its post-extend value `SHA-384(0x00..00 || SHA-384(elf))` is fully determined by the running ELF. - **Platform identity** (cloud-managed): `keccak256(MRTD || RTMR0 || RTMR1 || RTMR2)`, approved via the hook's `approvePlatform` admin. RTMR2 moves into the platform half because it measures TD initrd + kernel cmdline — both boot-image properties decided at TD launch by the firmware/boot chain, never the userspace Go binary loaded from disk after boot. Why not `keccak256(RTMR2)` for the binary half: on every direct-launch cloud TD measured, RTMR2 contains the TD initrd + cmdline digest. The story-kernel Go binary is loaded from the rootfs after boot and is never fed into RTMR2 by any link of the boot chain — so a `keccak256(RTMR2)` rule would commit to a boot-image artifact, not to the running ELF. The v3 split corrects this by claiming RTMR3 explicitly for the userspace binary half. This decouples binary release governance from cloud firmware vintage rotation, so each axis updates independently. `REPORT_DATA[0:32]` carries the kernel-bound caller commitment from `DKG.register` and is compared against `expectedDataCommitment`, mirroring SGX's instance binding. ## Onboarding flow A TDX enclave type is enabled in two steps: 1. Governance: `DKG.whitelistEnclaveType(enclaveType, { codeCommitment = keccak256(RTMR3), validationHookAddr = tdxProxy })`. 2. Hook owner: `TDXValidationHook.approvePlatform(keccak256(MRTD || RTMR0 || RTMR1 || RTMR2), label)`. ## Contract changes - `TDXValidationHook` (new). `_computeBinaryCommitment` reads `RTMR3` at offset 520; `_computePlatformCommitment` assembles the 192-byte preimage from MRTD (offset 184) + the contiguous RTMR0/RTMR1/RTMR2 block (offsets 376..519). - `ITDXValidationHook` (new). Admin surface: `setAutomataValidationAddr`, `approvePlatform`, `revokePlatform`, `isPlatformApproved`. - `IAutomataDcapAttestationFee`: adds 1-arg overload (PR #816 pattern) so standard TCB transitions take effect automatically. `DKG.sol` / `IDKG.sol` are unchanged — ABI/storage compatible with the live deployment. ## Adversary cost — both halves must succeed To pass `register()` an attacker must produce a TDX quote where: - `RTMR3` hashes to an approved binary commitment, **and** - `MRTD || RTMR0 || RTMR1 || RTMR2` hashes to an approved platform commitment. The RTMR3 self-extend can only push values *into* RTMR3 (RTMRs are append-only). An attacker binary `B'` that self-extends RTMR3 with `SHA-384(honest_B)` to forge the binary commitment still has to be loaded by the boot chain — which measures the kernel image + initrd into RTMR1 and the initrd + cmdline into RTMR2. Either modification shifts the platform commitment off any honest entry, and the platform-approval check fails.
b504b4b to
6a3ca9c
Compare
|
#68's 64/65 key-length asserts conflict with the now-merged #844 — please change them to 32/64 before merging. This PR pins
Pinning the lengths to keep the packed preimage injective is the right idea — just pin to the correct values. Before merging, please change:
See issue #73 and story PR #844 for the kernel↔contract alignment. |
b56583d to
b9dd9f2
Compare
bef9a75 to
89a3aa1
Compare
Extract the TEE-backend surface — code commitment, remote quote, and key sealing — into a single interface with build-tag-selected implementations, so a new backend can be added without changing call sites. Move the existing SGX/Gramine code under enclave/sgx, add a no-op backend for non-TEE builds, and split the sealed key store into enclave/sealdb. No behavioral change.
89a3aa1 to
302cbf1
Compare
…ment Implement the TDX backend behind the TEE interface: - Acquire a raw TDX v4 quote via configfs-tsm (direct, non-paravisor adapter) with a vendor/platform selection layer and a fail-closed fallback. - Derive the binary commitment from RTMR3, self-extended at init with the measurement of the running kernel binary (binary_commitment = keccak256(RTMR3)). - Bind key and DKG-state sealing to the measured boot via PCR 12, which the backend self-extends with the binary measurement when the initrd-side measurement is unavailable under the hardened image. - Pin the attestation report-data field sizes to the kernel's actual output and the contract's register (32-byte ed25519 dkgPubKey, 64-byte secp256k1 commKey). - Add production-image setup scripts, platform-commitment extraction tooling, and operator/auditor setup docs. Also bumps the CI setup-go action.
302cbf1 to
a60277c
Compare
|
Let's hold to merge this PR until releasing v0.2.0. |
Summary
Adds an Intel TDX backend to story-kernel and aligns the kernel-reported identity with Story's hybrid on-chain
TDXValidationHookunder schema v3 (RTMR3-bound binary commitment).enclave/tdx/, selectable via-tags tdx/make build-tdx. Implements the existingenclave.TEE/enclave.SealDBcontract soservice/,server/, andstore/stay backend-agnostic.enclave/tdx/platform/with the upstream Linuxconfigfs-tsmpath (directadapter) as the supported vendor. Runtime selection viaSTORY_TDX_VENDORwith auto-detect fallback throughProbe().supportedProviders) combined with an AES-GCM hybrid wrap, so the TPM only seals the data-encryption key. PolicyOR digest population is driven by a bootstrap-mode WARN log so operators can pin the empirically measured digest on first deploy.codeCommitment = keccak256(RTMR3)to match the hybrid hook's binary half. RTMR3 is self-extended by the kernel exactly once at startup withSHA-384(/proc/self/exe), so the post-extend valueSHA-384(0x00..00 || SHA-384(elf))is fully determined by the running ELF. The chain-side hook independently checks the platform halfkeccak256(MRTD || RTMR0 || RTMR1 || RTMR2)against itsapprovePlatformwhitelist; the kernel side does not gate on that.V4.report_datacarries a canary.How identity flows on chain
The matrix decomposes: N binaries × M platform vintages collapse from N×M into N + M governance rows, and each row tracks exactly one axis.