docs(tee): correct the Azure vTPM trust-anchor claim after hardware measurement - #75
Merged
Merged
Conversation
…easurement Ran the TPM path on a real Azure Trusted Launch vTPM (Standard_D2s_v7, eastus2, 2026-08-01). Most of what #74 shipped holds up on hardware. One thing I wrote there does not. tpm_roots.py described AZURE_VTPM_ROOT_2023_PEM as "the one root cA2A has validated on hardware". That is not true fleet-wide. On this host the AK certificate at NV 0x01C101D0 is 994 bytes, is issued by CN=Global Virtual TPM CA - 03, and carries no AIA extension at all. With nothing to walk, the collector ships the leaf alone; tpm2_getcap handles-nv-index showed only 0x01C101D0, so the intermediates are not in NV either. No chain reaches the pinned root and verify_tpm_report fails closed with "AK chain root is not among the supplied trusted TPM roots", which is the correct outcome since key provenance genuinely cannot be established there. The earlier observation (Standard_D2s_v5, eastus, 1596-byte certificate under Azure Cloud Virtual TPM CA - 11 with a walkable AIA chain to that root) is also real. Azure runs more than one vTPM CA generation, so a deployment must pin the hierarchy its own hosts present rather than assuming this one. What passed on hardware, now recorded rather than hedged: the collector produced a genuine platform-AK quote, the shipped certificate certifies the quoting key, parse_tpmt_signature unwrapped a real TPMT_SIGNATURE (RSASSA/SHA-256) and the bare signature verified against the shipped key, a tampered attest blob was rejected, and extraData equalled the derived key-and-nonce binding. Also retires the caveat that collector and verifier could not run in one process: building tpm2-pytss from source inside a venv resolves the conflict with agent-manifest's cryptography. The blocker on that host is the missing chain, not tooling. No code change; the verifier behaved correctly throughout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ran the TPM path on a real Azure Trusted Launch vTPM (
Standard_D2s_v7, eastus2, 2026-08-01) to validate what #74 shipped. Most of it holds up. One claim I wrote in #74 does not, so this corrects it.Docs only. The verifier behaved correctly throughout; there is no code change.
What passed on hardware
detect()on a vTPM hostattest()produces a quoteTPMT_SIGNATURE, 1403B chainparse_tpmt_signatureon real TPM output0x0014/ SHA-2560x000b, 256B bare signatureextraData== derived key-and-nonce bindingThe binding line is the one that matters most, since it is the core claim of #74: the key really is committed inside the signed structure on real hardware, not just in synthetic vectors.
What failed, and why it matters
verify_tpm_reportcould not chain to the pinned root:On this host the AK certificate at NV
0x01C101D0is:CN=Global Virtual TPM CA - 03, a different hierarchy than the pinned chainWith no AIA there is nothing to walk, so
_chain_from_leafcorrectly returns the leaf alone.tpm2_getcap handles-nv-indexshowed only0x01C101D0, so the intermediates are not stored elsewhere in NV either. Chained verification is therefore impossible on that host, and failing closed is the right outcome, since key provenance genuinely cannot be established.The earlier observation is also real: a
Standard_D2s_v5in eastus presented a 1596-byte certificate underAzure Cloud Virtual TPM CA - 11with a walkable AIA chain reachingAzure Virtual TPM Root Certificate Authority 2023. Azure runs more than one vTPM CA generation.So the claim I wrote in #74, that
AZURE_VTPM_ROOT_2023_PEMis "the one root cA2A has validated on hardware", is wrong as a general statement. The constant stays, now documented as one observed hierarchy rather than a guarantee, with the fleet variance recorded in the module,LIMITATIONS.md, and the attestation spec. A deployment must pin the hierarchy its own hosts actually present.One caveat retired
LIMITATIONS.mdpreviously said collector and verifier could not run in one process, because installingagent-manifestconflicts with the distribution'stpm2_pytss. Buildingtpm2-pytssfrom source inside a venv fixes it:pytss + agent-manifest + cryptography 49.0.0coexisted and both halves ran in one interpreter. The blocker on that host is the missing certificate chain, not tooling.Net effect on the TPM tier
Evidence is genuine, and its signature and binding are verifiable. Key provenance is host-dependent. That is a weaker claim than #74 implied and a more useful one to hold.
🤖 Generated with Claude Code